Incomaker Commons v4
API Reference
API Reference
  1. Webhooks
  • Incomaker Commons v4
  • Sklik audience response validation
  • Social Accounts
    • Get Accounts
      GET
  • Ad Accounts
    • Get Accounts for Facebook
      GET
  • Assets
    • Generate e-mail content (AI)
      POST
  • Audiences
    • Get Audiences
      GET
  • Campaigns
    • Get campaigns
      GET
    • Archive Campaign
      POST
    • Pause/Resume/Cancel Campaign
      POST
    • Rename Campaign
      PUT
    • Duplicate Campaign
      POST
    • Delete Campaign
      DELETE
    • Get Categories
      GET
    • Add New Category
      POST
  • Contacts
    • Get contact by clientContactId
    • Get contact by email
    • Add contact to the list by id
    • Remove contact from the list by id
    • Add contact to the list by name
    • Remove contact from the list by name
    • Get contact by phoneNumber1
    • Upsert multiple contacts (async)
    • Get contact events
    • Get contact events by client id
    • Get contact events by email
    • Partially update contact
    • Get all contacts
    • Create contact
    • Upsert contact
    • Get contact
    • Update contact
    • Delete contact
    • Get total number of contacts
    • Upsert multiple contacts
    • Delete multiple contacts
    • Contact login
    • Contact logout
    • Get contact`s subscriptions
    • Replace contact's subscriptions (full set)
    • Adjust contact's subscriptions (partial)
    • Unsubscribe a contact from all lists
  • Carts
    • Add an Item to Cart
    • Remove an Item from Cart
    • Create an Order from Cart (checkout)
  • Deals
    • Get deal
    • Update deal
    • Delete deal
    • Find deals
    • Create deal
    • Get deals attachments
    • Post deals attachment
    • Delete deals attachment
    • Get deals contacts
    • Add deals contacts
    • Remove contact from deal
    • Get deals activities
    • List deal states
    • Create deal state
    • Update deal state
    • Delete deal state
  • Delivery
    • Send E-mails delivery
    • Send SMS delivery
    • Send push delivery
    • Send Test E-mail
    • Send Test SMS
    • Send Test Push
    • Validate E-mail (dry run)
    • Validate SMS (dry run)
    • Validate Push (dry run)
    • Get Delivery Job
    • List Delivery Jobs
    • Cancel Delivery Job
    • Pause Delivery Job
  • Events
    • Add event with contact
    • Add tracking event
    • Get event types
  • Forms
    • Get form settings
    • Update form settings
    • Get form field mappings
    • Update form field mappings
  • Notifications
    • Get notifications
    • Get notification
    • Modify notification
    • Delete notification
  • Lists
    • Get Lists
    • Create List
    • Get List
    • Update List
    • Delete List
    • Get List by name
    • Update List by name
    • Delete List by name
    • Get number of contacts for a list
    • Get number of contacts for a list by name
    • Get contact counts for all lists (grouped by category)
    • Upsert Lists (bulk)
    • Get List categories
    • Create List category
    • Get List category
    • Update List category
    • Delete List category
    • Get List category by name
    • Update List category by name
    • Delete List category by name
    • Upsert List categories (bulk)
  • Products
    • Get total number of products
    • Delete multiple products
    • Get variant
    • Get products
    • Get products categories
    • Get tags
    • Create product
    • Upsert product
    • Upsert multiple products (sync)
    • Upsert multiple products (async job)
    • Get product
    • Replace product
    • Partially update product
    • Delete (deactivate) product
    • Upsert single variant
    • Partially update variant
    • Delete (deactivate) variant
    • Add tag to product
    • Remove tag from product
    • Add product to category
    • Remove product from category
    • Create product category
    • Upsert product category
    • Upsert multiple product categories
    • Get product category
    • Replace product category
    • Partially update product category
    • Delete (hide) product category
  • Orders
    • Get orders
    • Create order
    • Upsert multiple orders
    • Get order
    • Update order
    • Update order state
    • Delete order
    • Get order by client ID
    • Update order by client ID
    • Update order state by client ID
    • Delete order by client ID
  • Scenarios
    • Abandoned cart notification by e-mail
    • Abandoned cart notification by SMS
    • Abandoned cart notification by push
    • Abandoned cart notification by Facebook
    • Create birthday greetings
    • Create nameday greetings
    • Create first purchase anniversary
  • Smartflows
    • Triggers
      • Start
        • Get Start trigger
        • Delete Start trigger
        • Create Start trigger
      • Added to list
      • Removed from list
      • Scheduled start
        • Get Scheduled start trigger
        • Update Scheduled start trigger
        • Delete Scheduled start trigger
        • Create Scheduled start trigger
      • Recurring
        • Get Recurring trigger
        • Update Recurring trigger
        • Delete Recurring trigger
        • Create Recurring trigger
      • Nameday
        • Get Nameday trigger
        • Update Nameday trigger
        • Delete Nameday trigger
        • Create Nameday trigger
      • Birthday
        • Get Birthday trigger
        • Update Birthday trigger
        • Delete Birthday trigger
        • Create Birthday trigger
      • First registration
        • Get First registration trigger
        • Update First registration trigger
        • Delete First registration trigger
        • Create First registration trigger
      • First purchase
        • Get First purchase trigger
        • Update First purchase trigger
        • Delete First purchase trigger
        • Create First purchase trigger
      • Email subscription confirmed
        • Get Email subscription confirmed trigger
        • Update Email subscription confirmed trigger
        • Delete Email subscription confirmed trigger
        • Create Email subscription confirmed trigger
      • Link click
        • Get Link click trigger
        • Update Link click trigger
        • Delete Link click trigger
        • Create Link click trigger
      • Mail open
        • Get Open mail trigger
        • Update Open mail trigger
        • Delete Open mail trigger
        • Create Open mail trigger
      • Product added to cart
        • Get Product added to cart trigger
        • Update Product added to cart trigger
        • Delete Product added to cart trigger
        • Create Product added to cart trigger
      • Custom event
        • Get Custom event trigger
        • Update Custom event trigger
        • Delete Custom event trigger
        • Create Custom event trigger
      • Abandoned cart
        • Get Abandoned cart trigger
        • Update Abandoned cart trigger
        • Delete Abandoned cart trigger
        • Create Abandoned cart trigger
    • Actions
      • Sklik Audience
        • Get Sklik audience action
        • Update Sklik audience action
        • Delete Sklik audience action
        • Create Sklik audience action
        • Connect Sklik account
        • Disconnect Sklik account
        • Get Sklik audiences for action
        • Create Sklik audience
      • Meta audience
        • Create Meta audience
        • Get Meta audience action
        • Update Meta audience action
        • Delete Meta audience action
        • Create Meta audience action
        • Get Meta audiences for action
        • Connect Meta Account
        • Disconnect Meta account
        • Get list of business portfolios
        • Get list of advertising accounts
      • GoogleAds Audience
        • Create GoogleAds audience
        • Get GoogleAds audience action
        • Update GoogleAds audience action
        • Delete GoogleAds audience action
        • Create GoogleAds audience action
        • Get GoogleAds audiences for action
        • Connect GoogleAds Account
        • Disconnect GoogleAds account
        • Get list of users
      • Double Opt-In
        • Get Double Opt-In action
        • Update Double Opt-In action
        • Delete Double Opt-In action
        • Create Double Opt-In action
        • Swap branches
      • Add contact to list
        • Get Add contact to list action
        • Update Add contact to list action
        • Delete Add contact to list action
        • Create Add contact to list action
      • Remove contact from list
        • Get Remove contact from list action
        • Update Remove contact from list action
        • Delete Remove contact from list action
        • Create Remove contact from list action
      • Teaser
        • Get Teaer action
        • Create Teaer action
        • Delete Teaer action
      • Add tag to contact
        • Get Add tag action
        • Update Add tag action
        • Delete Add tag action
        • Create Add tag action
      • Remove tag from contact
        • Get Remove tag action
        • Update Remove tag action
        • Delete Remove tag action
        • Create Remove tag action
      • Notification
        • Get Send notification
      • Website Widget
        • Get Website Widget action
        • Create Website Widget action
        • Update Website Widget action
        • Delete Website Widget action
    • Targets
    • Activities
      • Convert activity
      • Duplicate activity
      • Enable/Disable activity
      • Update activity shared fields
    • Structure
      • Get smartflow structure
      • Delete a part of smartflow
    • Get Added to contact list trigger
    • Get smartflows
    • Update Added to contact list trigger
    • Create smartflow
    • Delete Added to contact list trigger
    • Get smartflow
    • Create Added to contact list trigger
    • Update smartflow
    • Get Removed from contact list trigger
    • Delete smartflow
    • Update Remove from contact list trigger
    • Delete Remove from contact list trigger
    • Generate smartflow (AI)
    • Create Removed from contact list trigger
    • Pause/Resume/Cancel smartflow
    • Execute smartflow
    • Validate smartflow
    • Export smartflow
    • Import smartflow
    • Duplicate smartflow
    • Get smartflow actions
    • Archive smartflow
    • Get templates
    • Create template
    • Get template actions
    • Get folders
    • Create folder
    • Modify folder
    • Delete folder
    • Get Product in Cart is back in Stock trigger
    • Update Product in cart is back in stock trigger
    • Delete Product in cart is back in stock trigger
    • Create Product in cart is back in stock trigger
    • Get Product in Cart discounted trigger
    • Update Product in Cart discounted trigger
    • Delete Product in Cart discounted trigger
    • Create Product in Cart discounted trigger
    • Get Product in cart running out trigger
    • Update Product in cart running out trigger
    • Delete Product in cart running out trigger
    • Create Product in cart running out trigger
    • Get Product purchased trigger
    • Update Product purchased trigger
    • Delete Product purchased trigger
    • Create Product purchased trigger
    • Get Recently purchased trigger
    • Update Recently purchased trigger
    • Delete Recently purchased trigger
    • Create Recently purchased trigger
    • Get Checkout visited trigger
    • Update Checkout visited trigger
    • Delete Checkout visited trigger
    • Create Checkout visited trigger
    • Get Order cancelled trigger
    • Delete Order cancelled trigger
    • Create Order cancelled trigger
    • Get Invoice confirmation trigger
    • Delete Invoice confirmation trigger
    • Create Invoice confirmation trigger
    • Get Return confirmation trigger
    • Delete Return confirmation trigger
    • Create Return confirmation trigger
    • Get Website search trigger
    • Update Website search trigger
    • Delete Website search trigger
    • Create Website search trigger
    • Get Campaign sent trigger
    • Update Campaign sent trigger
    • Delete Campaign sent trigger
    • Create Campaign sent trigger
    • Get Shipping confirmed trigger
    • Update Shipping confirmed trigger
    • Delete Shipping confirmed trigger
    • Create Shipping confirmed trigger
    • Get Website Viewed trigger
    • Update Website Viewed trigger
    • Delete Website Viewed trigger
    • Create Website Viewed trigger
    • Get Filter target
    • Update Filter target
    • Delete Filter target
    • Create Filter target
    • Get Merge target
    • Delete Merge target
    • Create Merge target
    • Get Split target
    • Update Split target
    • Delete Split target
    • Create Split target
    • Get Splitter target
    • Update Splitter target
    • Delete Splitter target
    • Create Splitter target
    • Get Condition target
    • Update Condition target
    • Delete Condition target
    • Create Condition target
    • Get A/B test action
    • Update A/B test action
    • Delete AB test target
    • Create AB test target
    • Get Send mail action
    • Update Send email action
    • Delete Send mail action
    • Create Send mail action
    • Get Send sms action
    • Update Send sms action
    • Delete Send sms action
    • Create Send sms action
    • Get Send social post action
    • Update Send social post action
    • Delete Send social post action
    • Create Send social post action
    • Get Send NPS action
    • Update Send NPS action
    • Delete Send NPS action
    • Create Send NPS action
    • Get Webhook action
    • Update Webhook action
    • Delete Webhook action
    • Create Webhook action
    • Get Delay action
    • Update Delay action
    • Delete Delay action
    • Create Delay action
    • Get Landing page action
    • Update Landing page action
    • Delete Landing page action
    • Create Landing page action
    • Get Unsubscribe action
    • Update Unsubscribe action
    • Delete Unsubscribe action
    • Create Unsubscribe action
    • Get list of contact fields to update
    • Get Update contact action
    • Update Update contact action
    • Delete Update contact action
    • Create Update contact action
    • Get Popup action
    • Update Popup action
    • Delete Popup action
    • Create Popup action
  • Website
    • Get Website Info
    • Get Teaser settings
  • Tags
    • Get Tags
    • Create Tag
    • Update Tag
    • Get Tag
    • Delete Tag
    • Get number of contacts for a tag
    • Get contact counts for all tags (grouped by category)
    • Create Tags (bulk)
    • Get Tag categories
    • Create Tag category
    • Get Tag category
    • Update Tag category
    • Delete Tag category
    • Create Tag categories (bulk)
  • Context
    • Get Context
    • Update Context
    • Replace Context
  • Admin
    • Refresh Currency Rates
  • Jobs
    • Get job status
    • List jobs
    • List job items
    • Cancel job
  • SMS
    • Get SMS Templates
    • Get SMS Templates History
    • Create SMS Template
    • Get SMS Senders
  • Coupons
    • Get Coupons
    • Get Coupons types
    • Get Coupon type
  • Tracking Links
    • Create Tracking Link
  • Webhooks
    • Webhooks — Usage Guide
    • List webhook topics
      GET
    • Create webhook
      POST
    • List webhooks
      GET
    • Get webhook
      GET
    • Update webhook
      PATCH
    • Delete webhook
      DELETE
    • Test-send webhook
      POST
    • List deliveries for a webhook
      GET
    • Get delivery detail
      GET
  • Messages
    • Send e-mail
    • Send e-mails (batch)
    • Validate e-mail (dry run)
    • Send SMS
    • Send SMS (batch)
    • Validate SMS (dry run)
    • List messages
    • Get message
    • Cancel scheduled message
    • Send e-mails (batch, async)
    • Send SMS (batch, async)
    • Send e-mail to an audience
    • Send SMS to an audience
  • Billing
    • Create billing settings
    • Update billing settings
    • Get billing settings
  • List domains
    GET
  • Add domain
    POST
  • Get DNS records
    GET
  • Get link shorteners
    GET
  • Update link shorteners
    PUT
  • Verify DNS changes
    POST
  • Get eshop settings
    GET
  • Update eshop settings
    PUT
  • Get Smartflow Validation Rules
    GET
  • Get invoices
    GET
  • Get invoices
    GET
  • Get custom contact field placeholders
    GET
  • Get contact placeholders
    GET
  • Get system placeholders
    GET
  • Get account placeholders
    GET
  • Get user placeholders
    GET
  • Get external placeholders
    GET
  • Get coupon placeholders
    GET
  • Get media
    GET
  • Upload media
    POST
  • Get media item
    GET
  • Update media metadata
    PUT
  • Delete media
    DELETE
  • Get media tags
    GET
  • Schemas
    • Audience
    • Notification
    • Error
    • CampaignType
    • Contact
    • Product (read)
    • ProductWriteNoId
    • Product tag
    • ProductCategoryWriteNoId
    • Price
    • CampaignChannel
    • CampaignState
    • Campaign
    • CustomField
    • Context
    • ProductLanguage
    • ProductVariantWriteNoId
    • CreateActionResponse
    • ProductVariantWrite
    • TeaserFontFamily
    • TeaserFontWeight
    • ProductWrite
    • TeaserCloseButtonSize
    • TeaserCloseButtonPosition
    • ProductVariant
    • TeaserPosition
    • NotificationSeverity
    • CategoryLanguage
    • NotificationChannel
    • Tag
    • ProductCategoryWrite
    • TagCategory
    • ProductCategory
    • List
    • UpsertResult
    • ListCategory
    • VariantUpsertResult
    • CategoryUpsertResult
    • SmartflowTemplateAction
    • BulkResult
    • SmartflowAction
    • CategoryBulkResult
    • SmartflowState
    • SmartflowCompletedState
    • SocialNetworkType
    • SocialNetworkEntityType
    • SocialNetworkEntity
    • SocialNetworkUser
    • Smartflow
    • SmartflowFolder
    • SocialAccount
    • AudienceInput
    • EmailAsset
    • SmsAsset
    • PushAsset
    • Cron
    • SocialAccountsResponse
    • AdAccount
    • AdAccountsResponse
    • AudiencesResponse
    • ProductsResponse
    • ProductsCategoriesResponse
    • ProductTagsResponse
    • SmartflowsResponse
    • SmartflowFoldersResponse
    • SmartflowActionsResponse
    • SmartflowTemplateActionsResponse
    • Paging
    • TeaserSettings
    • WebsiteSettings
    • General rules
    • PermId
    • Events
    • CouponType
    • Coupon
    • Media item
  1. Webhooks

Webhooks — Usage Guide

Everything you need to subscribe to Incomaker events and receive them reliably: how to create a
subscription, verify that a call really came from us, what happens when your receiver is down, and
what you can and can't configure yet. Read this before your first POST /v4/webhooks — the endpoint
reference in this folder covers shape, this page covers behavior.

1. Quickstart#

That's the whole loop. Everything below explains what happens between steps 1 and 3, and how to make
your receiver trustworthy and correct.
On <JWT> above. This API accepts Authorization: Bearer <token> with either a JWT issued by
the Incomaker token service, or a bare API key (commons-api exchanges it for a token on your
behalf on every request). A session header also works for browser/UI callers. JWT is the
preferred form — it skips the exchange round-trip and is what any existing commons-api
integration should already be sending — so the examples in this guide use it throughout.

2. Topics#

Call GET /v4/webhooks/topics for the live, current catalogue — don't hardcode a list, it grows over
time. As of this writing, nine topics exist, all describing something commons-api itself writes
(contacts and orders):
topicfires when
contact.createda new contact is created
contact.updatedan existing contact is updated
contact.deleteda contact is deleted
contact.unsubscribeda contact is removed from every subscription list (the "fully off" case — see below)
contact.list_addeda contact is added to any one list
contact.list_removeda contact is removed from any one list
order.createda new order is created
order.updatedan existing order is updated
order.cancelledan order's state changes specifically to cancelled (no other state transition fires this)
A subscription can list any subset. There's no wildcard syntax yet — list every topic you want
explicitly.
Two topics you might expect that don't exist, on purpose:
contact.subscribed. There's no single write in commons-api that unambiguously means "this
contact opted into marketing" as opposed to "this contact was added to list #7." Rather than guess,
we only ship the unambiguous half of the pair: contact.list_added for "added to a list" (generic,
correct for any list) and contact.unsubscribed for "removed from everything" (the one case that
really is unambiguous). If you need to detect opt-in specifically, watch contact.list_added and
filter on the list you care about client-side for now.
cart.abandoned. Detecting an abandoned cart means detecting the absence of a checkout after
some time window — that needs a scheduled sweep, not a webhook-style write-to-hook. Not available
yet.

3. The payload envelope#

Every delivery is a JSON POST (or PUT/PATCH, if you configured that). You can configure the shape of the payload - see 3b - or use a default payload format:
{
  "id": "whd_75b14900-1b9d-4cf1-910b-1ee7f014ed60",
  "type": "contact.unsubscribed",
  "timestamp": "2026-08-25T23:41:54.000Z",
  "payloadVersion": "amos",
  "websiteId": "3f2a9c4e-8b1d-4a6f-9c2e-7d5f1a8b3e6c",
  "data": {
    "contact": {
      "contactId": "0f3e2b1a-...",
      "clientContactId": "1042",
      "email": "jan@example.cz"
    },
    "lists": [{ "id": 7, "name": "Newsletter" }]
  },
  "links": {}
}
fieldmeaning
idStable identifier for this delivery, whd_-prefixed. Use it as your idempotency key — see §5. Also sent as the webhook-id header.
typeThe topic name, exactly as in the catalogue.
timestampWhen the underlying event actually happened — not when this specific delivery attempt was sent. Stable across retries.
payloadVersionSee §7 — always "amos" right now.
websiteIdThe website this event belongs to, as a uuid — the same identifier your own auth token carries. Only relevant if one endpoint somehow serves multiple websites; otherwise a constant you can ignore.
dataTopic-specific. See GET /v4/webhooks/topics's exampleData for every topic's shape — it's not identical across topics (e.g. order.* nests under order, not contact).
linksReserved; currently empty.
data.contact.clientContactId and data.order.clientOrderId are always present when you set them at
creation — prefer matching on these (your own identifiers) rather than the internal contactId/
orderId UUIDs when correlating a delivery back to your own records.

3b. Customizing the payload#

Don't want the envelope above? Set body (and optionally bodyFormat, currently only "json" is
supported) on the endpoint and we send that instead — rendered fresh for every event.
{
  "topics": ["contact.created"],
  "url": "https://shop.example.cz/api/webhooks/incomaker",
  "bodyFormat": "json",
  "body": {
    "contact": {
      "id": "{{ data.contact.contactId }}",
      "email": "{{ data.contact.email }}"
    }
  }
}
When contact.created fires, that delivers exactly:
{"contact":{"id":"e6308cf8-c972-11e9-ac23-ac1f6b0076ec","email":"vach@incomaker.com"}}
How it works:
body is a JSON object (stringified automatically) or a raw string — either form works.
{{path.to.field}} placeholders are resolved against the same envelope described in §3 —
id, type, timestamp, websiteId, and data.* (topic-specific; check
GET /v4/webhooks/topics's exampleData for the exact path names per topic, since they differ —
data.order.* for order topics, data.contact.* for contact ones).
This is plain string substitution, not a full templating language — no conditionals, no loops,
just "replace this placeholder with this value."
Rendered fresh on every delivery attempt, including retries — never frozen at creation time.
The signature covers the rendered body, not the envelope. If you set a custom body,
webhook-signature is computed over exactly what you configured, post-rendering — verify against
the bytes you actually receive, same as always (§4).
A template referencing a path that doesn't exist is a delivery failure, logged on the attempt
(GET /v4/webhooks/deliveries/{deliveryId}) — never a silently empty or broken body sent to you.
Omit body entirely to keep getting the canonical envelope from §3 — nothing changes for existing
endpoints that don't set it.

4. Verifying a delivery is genuine#

We sign every request per the Standard Webhooks convention, so
any off-the-shelf Standard Webhooks verification library works unmodified. Three headers are sent
with every request:
webhook-id:        whd_75b14900-1b9d-4cf1-910b-1ee7f014ed60
webhook-timestamp: 1787696517
webhook-signature: v1,K5oZfzN95Z4WsjTz+9nOJ9tHfE...
These are always present and cannot be disabled or overridden by anything you configure — signing
isn't optional.
What's signed: {webhook-id}.{webhook-timestamp}.{raw request body}, HMAC-SHA256, keyed by your
endpoint's secret (the whsec_... value you got back exactly once at creation), base64-encoded,
prefixed v1,.
Verify it yourself (Node.js, no library):
Two rules that matter for correctness, not just security:
1.
Verify against the raw, unparsed body bytes. If your framework's body parser has already
deserialized and re-serialized the JSON by the time your verification code runs, the bytes won't
match what we signed even though the content is logically identical (key ordering, whitespace).
Hook verification in before any body-parsing middleware, or use your framework's raw-body option.
2.
Check the timestamp, don't just check the signature. webhook-timestamp is a Unix-seconds
value; reject anything outside a tolerance window you're comfortable with (a few minutes is
typical) to protect against a captured request being replayed later. The signature alone doesn't
protect against replay — the timestamp check does.

5. Idempotency#

Delivery is at-least-once, not exactly-once. A retried delivery (see §6) reuses the same
webhook-id/id across every attempt — that's the whole point of it being stable — so use
webhook-id as your deduplication key
on your side. Store the ids you've already processed (even a
short-TTL cache is enough) and skip anything you've seen before you apply its side effects.

6. Delivery, retries, and failure#

Success is any 2xx status code from your receiver. Anything else is a failure, and failures
split into two very different buckets:
Retryable: 408, 429, any 5xx, or the request not completing at all (timeout, connection
refused, DNS failure). These get retried automatically.
Terminal: any other 4xx (400, 401, 403, 404, 422, ...). These are not retried —
if your receiver rejects the payload as malformed or unauthorized, retrying the identical request
won't help, so we don't hammer you with it. Fix the underlying issue and the next real event will
succeed; the failed one stays dead.
Retry ladder (retryable failures only), roughly: immediately, then 15s, 1m, 5m, 30m, 2h, 6h, 12h
later — 8 attempts spread across about 21 hours before a delivery is marked dead and abandoned.
Exact timings are an operational default and may be tuned; treat "roughly a day, front-loaded toward
fast retries" as the guarantee, not the specific numbers.
Timeout: we wait 15 seconds for your receiver to respond before treating the attempt as failed
(and retryable, per the bucket above).
Ordering: deliveries are not guaranteed to arrive in the order their underlying events
occurred. If your integration needs strict ordering (e.g. contact.updated must never be processed
before an earlier contact.created for the same contact), use data.contact.contactId +
timestamp to reorder on your side rather than assuming arrival order.
If your endpoint keeps failing, it will eventually be auto-disabled after enough consecutive
failures across separate deliveries, to stop us calling a URL that's clearly not going to work. Check
GET /v4/webhooks/{id} for "status": "disabled" if deliveries seem to have silently stopped, and
PATCH it back to "enabled" once you've fixed the receiver — nothing is lost, matching events
during the disabled window simply weren't delivered and won't be automatically replayed (see
"What's not here yet" below).
Debugging a specific failure: GET /v4/webhooks/{id}/deliveries?status=dead (or failed) to
find it, then GET /v4/webhooks/deliveries/{deliveryId} for the full per-attempt log — every
attempt's status code, timing, and your receiver's actual response body. Authorization and
webhook-signature are redacted in the logged request headers even though they were genuinely sent
— the log is for diagnosing your receiver's behavior, not for replaying a call with live
credentials.

7. Payload versioning#

payloadVersion is a plain codename — "amos" right now — not a date, and not "v1". Two
deliberate choices behind that:
Not a REST version. The /v4/ in every path here is a different axis entirely: it versions
this API's request/response shapes for managing subscriptions. payloadVersion versions the
event envelope shape sent to your receiver. They change independently — a future /v5/ wouldn't
necessarily touch payload shape, and a payload change wouldn't require a new REST version.
Not a date. The envelope already carries a real timestamp field; a date-shaped version string
sitting right next to it would be genuinely confusing (which date is which?). A codename can't be
mistaken for either.
An endpoint is pinned to whatever payloadVersion was current when it was created and keeps
receiving that shape indefinitely — a version doesn't change under you. There is currently exactly
one version, so this only matters as forward-looking context for when a second one ships.

8. Securing the target URL#

Every URL is checked, both when you create/update the endpoint and again on every actual send
(since DNS can change after creation):
HTTPS required outside local development environments. Plain http:// is only accepted where
the platform is explicitly configured for it (e.g. testing against a local tunnel).
Private, loopback, link-local, carrier-grade-NAT, and cloud-metadata addresses are rejected —
including 169.254.169.254 specifically — along with localhost and any *.internal /
*.local / *.svc.cluster.local / *.cluster.local hostname.
Redirects are never followed. If your URL responds with a 3xx, that's treated as a non-2xx
response (see §6), not as "go here instead."
A URL that fails validation at creation time returns 400 with a specific reason immediately — you
will never end up with an endpoint that silently never delivers because its target was unsafe from
the start.

9. Authentication to your receiver#

Beyond the mandatory signature (§4), you can optionally add:
A bearer token — "auth": {"type": "bearer", "token": "..."} at creation, sent as
Authorization: Bearer <token> on every delivery. Stored encrypted; never returned by any GET, the
same guarantee as the signing secret.
Static extra headers — "headers": [{"key": "X-Tenant", "value": "cz"}], sent verbatim on
every delivery, in addition to the signature headers. You cannot use this to override
webhook-id/webhook-timestamp/webhook-signature/Content-Type — those are always ours.
Basic auth, API-key auth, and OAuth2 aren't available yet — bearer or nothing are the only choices
today.

10. Limits (today)#

One event, one call. Batching multiple events into a single delivery isn't available —
batch.max must be 1 (the default); sending anything else is rejected at creation.
Website-scoped only. An endpoint belongs to exactly one website. Subscribing once across every
website in an account isn't available yet ("scope": "account" is rejected).
filter is accepted but not enforced. You can send a filter object at creation/update and it
will be stored, but nothing currently evaluates it — every event matching your topics is
delivered regardless of what's in filter. Don't rely on it to narrow volume yet.

11. What's not here yet#

Worth knowing about so you don't go looking for it:
Custom events (your own event names) and email/campaign events (opens, clicks, bounces,
sends) aren't subscribable topics yet — only the nine contact/order topics in §2 exist.
No manual replay. A dead delivery cannot currently be resubmitted through the API; you'd need
to wait for the next real occurrence of that event.
No secret rotation without deletion. If you lose your signing secret, the only recovery today
is deleting the endpoint and creating a new one (a fresh secret each time).
No form-encoded or XML body, no URL templating. JSON body customization exists (§3b), but
form-encoded/XML bodies and interpolating values into the URL itself are not built.
None of this affects the reliability guarantees above (signing, retries, delivery logging) — it's
scope, not stability, that's still growing.

12. Quick troubleshooting checklist#

Nothing arrives at all → check GET /v4/webhooks/{id} — is status still "enabled"? An
endpoint that failed enough times gets auto-disabled (§6).
Signature verification fails on your side → almost always a raw-body issue (§4, rule 1) — check
whether your framework already parsed the body before your verification code runs.
Some events arrive, others don't → check whether the missing ones are actually a topic you
subscribed to (GET /v4/webhooks/{id}'s topics array) — a common mistake is subscribing to
contact.list_added while expecting general "subscribed" events (§2).
A specific delivery never succeeded → GET /v4/webhooks/deliveries/{deliveryId} for the exact
status code and body your receiver returned on each attempt (§6).
Your receiver gets a body that doesn't match your body template, or an error instead → check
the attempt log for a render error (§3b) — usually a {{path}} that doesn't exist for that topic;
compare against GET /v4/webhooks/topics's exampleData for the exact field names.
Modified at 2026-08-27 16:56:05
Previous
Create Tracking Link
Next
List webhook topics
Built with