Skip to main content

media.delivery.completed

Composio upload of listing media to the agent's cloud storage finished successfully. summary.files_failed may be > 0 in the partial-success case (some files uploaded, some files skipped) - this is still considered a completion.

Category: Media

Emitted when: deliver_media_to_storage Celery task returns success (app/tasks/composio_tasks.py).

Envelope

Every delivery wraps the event-specific data block in the canonical envelope documented on the webhook overview page. The body carries SIX keys; the HMAC signature is delivered as the X-Webhook-Signature request header on the inbound POST to your endpoint (NOT as a body field; including it in the body would make the HMAC a function of its own output).

FieldTypeDescription
event_idstringULID; globally unique. Safe to persist as the dedup key.
event_typestringAlways media.delivery.completed for this event.
api_versionstringPayload schema version (YYYY-MM-DD).
timestampintegerUnix epoch seconds at dispatch time.
noncestringULID; unique per delivery, used for replay rejection.
dataobjectEvent-specific payload (see below).

Delivery also sets three HTTP headers on the inbound POST:

HeaderMeaning
X-Webhook-Event-IdMirrors event_id; cheaper than parsing the body to dedup.
X-Webhook-TimestampMirrors timestamp for replay-window checks.
X-Webhook-Signaturesha256=<hex> HMAC over timestamp.body.

Data schema

FieldTypeRequiredDescription
agency_idstringyesAgency Id
listing_numberstringyesPublic listing number, 7-char lowercase nanoid ([a-z0-9]{7})
job_idstringyes'deliver_' - stable across Celery retries of the same top-level invocation.
destinationobjectyesWhere the media bundle was uploaded to. folder_path is the human-visible path within the storage provider's root (e.g. "/Listings/HMREK1T"). Must be pre- sanitized per CLAUDE.md rule 30 before being placed on the payload; the validator below is a defence-in-depth check that would surface a buggy emission site in tests rather than leak malformed paths to integrators.
summaryobjectyesAggregate counts for the delivery job. Soft constraint (NOT enforced on the model because we do not carry the original batch size into the payload): files_uploaded + files_failed <= original_batch_size. That constraint is enforced at the call site, which has access to the batch size before emission. Using StrictInt rejects bool values (True would otherwise coerce to 1 and masquerade as a real count). Negative counts are rejected by ge=0.
duration_secondsintegeryesDuration Seconds

Sample payload

The sample below is a live fixture. Feed the exact body bytes shown plus the timestamp into the HMAC-SHA256 recipe from the webhook overview using the documented test-vector secret (test_secret_001 ) and you will recover the signature header value printed below.

Body (delivered as the HTTP request body):

{
"event_id": "evt_6CS9YQE76ND4JPG6BD1RDAGXAT",
"event_type": "media.delivery.completed",
"api_version": "2026-04-17",
"timestamp": 1745339401,
"nonce": "285BX2EMYT9AD69QT3J4RSK7FS",
"data": {
"agency_id": "user_01HXAGENCY0000000000000",
"listing_number": "abc1234",
"job_id": "01HX5Y7Z2M3N4P5Q6R7S8T9U0V",
"destination": {
"kind": "google_drive",
"folder_path": "/Listings/123-Main-St-Portland/Photos"
},
"summary": {
"files_uploaded": 0,
"files_failed": 0,
"total_bytes": 0
},
"duration_seconds": 0
}
}

Signature header (delivered as X-Webhook-Signature on the same request):

sha256=f94c522ee2969ecb0d2787a47caf7a1bcd3491ec574726bf76e87ed2e6fe18f5

Retry behaviour

Failed deliveries retry on the sequence 2, 4, 8, 16, 32 seconds (five attempts plus the initial call for a total of six over approximately 62 seconds of wall clock). After the final attempt the delivery lands in the dead-letter queue and the dashboard operator can replay it from Settings > API Keys > Webhook Endpoint > Deliveries. See the webhook overview for the full rules.

Verification

Every receiver MUST verify the X-Webhook-Signature header using the recipe in the webhook overview. Rejecting deliveries whose timestamp is more than 5 minutes off your wall clock (after NTP correction on your side) or whose nonce has already been consumed in the last 10 minutes is part of the contract.