Skip to main content

adapters

POST /adapters/{provider}/{src_id}​

Provider webhook receiver

Served on the ingestion host (https://ingest.staging.tessera.zone/v1/adapters/\{provider\}/\{src_id\}, WP-S7). Provider-authenticated — the request must pass the provider's signature verification against the source's decrypted webhook secret before any body processing beyond raw-byte reading (Stripe: Stripe-Signature header t=\<ts\>,v1=\<hex\> where v1 = HMAC-SHA256(secret, "\{t\}.\{raw_body\}"), 5 min tolerance; Shopify: X-Shopify-Hmac-Sha256 = base64 HMAC-SHA256 of the raw body; Amplitude: Authorization: Bearer \<webhook_secret\>; Mixpanel: Authorization: Basic base64("tessera:\<webhook_secret\>") (WP-S9 — neither provider signs payloads, so transport security is TLS plus this shared credential); constant-time comparison; failures → 401 with the 003 §1 envelope). No API keys, no CORS. Amplitude and Mixpanel deliveries (WP-S9) carry one provider event as a JSON object, a JSON array, an \{"events": [...]\} wrapper, or NDJSON (Content-Type: application/x-ndjson), at most 500 events / 5 MiB; an over-cap or unparseable body answers 400 invalid_argument with zero publishes, per-event outcomes are independent, and any publish failure answers 503 unavailable so the provider retries (safe under event-id dedup). An unknown provider, or a src_id that does not exist, is disabled, or whose provider mismatches, answers 404 without revealing which condition failed. The payload is mapped through the source's pinned catalog version merged with its overrides: mapped events publish to events.ingest (200 only after publish acknowledgment) and meter events_ingested; unmapped or skip_if-matched deliveries are acknowledged and dropped (counted); mapping failures on a mapped entry dead-letter with reason=mapping_failed and still answer 200 — provider-side retry cannot fix a mapping bug. Responses complete in < 3 s p99.

Operation ID: receiveAdapterWebhook · Service: ingest-api

Authentication​

None — this operation is public.

Parameters​

NameInRequiredTypeDescription
providerpathyesstringWebhook provider. This release registers stripe, shopify (WP-S7), amplitude and mixpanel (WP-S9); any other value answers 404 (WP-S7 req 2/12).
src_idpathyesstringThe adapter source id (src_ ULID; a kind=stream, subtype=adapter source). The id is validated against the source record — never trusted as tenancy (003 §3).

Responses​

StatusSchemaDescription
200AdapterWebhookReceiptDelivery acknowledged. outcome reports what happened (published | unmapped | skipped | deadlettered) — providers only need the 200.
defaultErrorEnvelopeError response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying.

Errors​

Every non-2xx response uses the 003 §1 error envelope; error.request_id matches the X-Request-Id response header, and the HTTP status always matches error.code:

{
"error": {
"code": "invalid_argument",
"details": [
{
"type": "field_violation"
}
],
"message": "human-readable",
"request_id": "req_01hxample"
}
}

Closed error-code set (003 §1): invalid_argument, unauthenticated, permission_denied, not_found, already_exists, failed_precondition, resource_exhausted, internal, unavailable.

resource_exhausted responses (HTTP 429) additionally carry retry_after_seconds. See ErrorEnvelope.