Push · AsyncAPI Specification

Push Webhooks

Version

View Spec View on GitHub CompanyCRMCustomer Data PlatformMarketing AutomationHospitalityHotelsGuest ExperienceEmailSMSPush NotificationsWebhooksSegmentationAsyncAPIWebhooksEvents

AsyncAPI Specification

Raw ↑
generated: '2026-08-13'
method: searched
source: https://developers.cendyncrm.com/api/webhooks
superseded_source: http://developers.pushtech.com/api/webhooks
superseded_note: >-
  The host this artifact was originally read from, developers.pushtech.com, no longer resolves
  (NXDOMAIN as of 2026-08-13). Re-read in full from the successor portal at
  developers.cendyncrm.com, which carries a substantially richer document than was captured in
  the first round: per-group payload field dictionaries, worked JSON examples, subscriber status
  code semantics, the exact retry schedule, and published signature-verification code.
type: Webhooks
api: Cendyn CRM (PUSHTech) REST API
asyncapi_document: asyncapi/push-webhooks-asyncapi.yml
delivery: HTTP POST callback to subscriber-configured URLs
subscription:
  mechanism: >-
    Callback URLs are configured by hand in the Webhooks section of the API area of the Cendyn CRM
    account manager. There is no subscription API — an agent cannot register, list or remove a
    webhook endpoint programmatically.
  granularity: >-
    Endpoints are registered per event group: by channel for delivery messages, by type for
    contact activities.
subscriber_response_contract:
  note: The status codes the provider reads back from the subscriber. This is the only documented status-code semantics on the entire surface.
  codes:
  - {status: 200, meaning: Success, retried: false}
  - {status: 406, meaning: Rejected by the subscriber, retried: false}
  - {status: any other, meaning: Failure, retried: true}
retries:
  window: 4 hours
  schedule: [5m, 5m, 10m, 10m, 30m, 1h, 2h]
  exceptions:
  - Delivery-channel events are never retried, regardless of the response code.
security:
  common_body_parameters:
  - {name: account_id, type: string, location: body, description: Your account ID}
  - {name: timestamp, type: integer, location: body, description: Seconds since 1970-01-01}
  - {name: token, type: string, location: body, description: Randomly generated string, length 50}
  signature:
    header: Authorization
    type: string
    encoding: hex digest
    algorithm: HMAC-SHA256
    signed_payload: concat(timestamp, token)
    key: account secret key
    verification: >-
      Concatenate the timestamp and token body values, HMAC-SHA256 the result using the account
      secret key, hex-encode it, and compare to the Authorization header. Optionally check the
      timestamp is close to the current time to bound replay.
    published_samples: [PHP, Python, Ruby]
    note: >-
      The signature covers only timestamp + token — it does NOT cover the event body. A valid
      signature therefore proves the request came from Cendyn CRM but does not prove the payload
      was not altered in transit. Subscribers should rely on TLS for body integrity and should
      enforce a timestamp freshness window, which the docs describe as optional.
event_groups:
- id: activities
  description: Contact interactions and behaviours, emitted from the API, the SDKs or a campaign.
  discriminator: type
  events: [event, update, custom, open_app, uninstall_app, subscribe_push, unsubscribe_push, add_cart, remove_cart, product_view, product_refund, purchase_failed, gps_location, contact_status]
  origins: [api, sdk, campaign]
  event_types: [opened, delivered, clicked, unsubscribed, sent, failed]
  payload_fields: [created_at, _id, origin, type, event_type, updated_field, updated_field_value, custom, custom_key, custom_value, old_status, current_status, 'contact[_id]', 'contact[user_id]', 'contact[device_id]', 'contact[name_first]', 'contact[name_last]', 'contact[email]', 'contact[phone]', 'app[_id]', 'app[name]', 'app[icon]', 'campaign[_id]', 'campaign[name]', 'product[UUID]', 'product[name]', 'product[price]', 'product[currency]']
- id: deliveries
  description: Message delivery status across the SMS, push and email channels.
  discriminator: status
  channels: [sms, push, email]
  events: [none, queued, sent, deliverd, opened, clicked, rejected, undefined, undelivered, forbidden_country_code, failed, bounced, unsubscribed, dropped, complained]
  events_note: >-
    Copied verbatim from the published status list, including the provider's own typo "deliverd"
    and the placeholder value "undefined". An integrator matching on "delivered" will silently
    miss these events.
  payload_fields: [id, created_at, updated_at, channel, status, refunded, url, 'campaign[_id]', 'campaign[name]', 'template[_id]', 'template[name]', 'template_data[sender]', 'template_data[subject]', 'template_data[content]', 'template_data[reply_to]', 'landing_page_sms[_id]', 'landing_page_sms[name]', 'landing_page_sms[url]', 'landing_page_push[_id]', 'landing_page_push[name]', 'landing_page_push[url]', channel_data, 'contact[_id]', 'contact[user_id]', 'contact[name_first]', 'contact[name_last]', 'contact[email]', 'contact[phone]', 'app[_id]', 'app[name]', 'app[icon]', 'device[_id]', 'device[type]', 'device[device_push_token]']
  device_types: [ios, android, web]
  note: >-
    The `refunded` boolean signals that the account balance was credited back for an undelivered
    message — the only place the consumption model surfaces in an event.
- id: contacts
  description: Contact lifecycle changes.
  discriminator: action
  events: [create, channel_subscription, update, destroy]
  origins: [sdk, platform_individual, platform_bulk, validation, campaign, api]
  payload_fields: [action, origin, origin_sdk, contact, 'contact[id]', 'contact[user_id]', 'contact[changes]', 'contact[contact_url]', contacts_ids, user_ids, channel_subscription, 'channel_subscription[channel]', 'channel_subscription[blacklisted]', 'channel_subscription[origin]', 'channel_subscription[date]']
  note: >-
    On an update the payload carries only the changed keys in contact[changes] plus a
    contact[contact_url] pointing at the full REST resource — a deliberate thin-event-plus-fetch
    design. Bulk operations replace the single contact object with contacts_ids[] and user_ids[]
    arrays.
- id: bulk_contacts
  description: Result notification for a batch contact import, with success and failure counts.
  payload_fields: [contacts_valid_count, contacts_failed_count, failed_contacts, 'failed_contacts[contact]', 'failed_contacts[error]', valid_contacts, 'valid_contacts[_id]', 'valid_contacts[user_id]', 'valid_contacts[email]', 'valid_contacts[phone_countrycode]', 'valid_contacts[phone_number]']
  note: >-
    This is the asynchronous completion event for createBulkContact. The field dictionary names
    failed_contacts[error] (singular) but the published example emits "errors" as an array —
    a documented inconsistency, recorded verbatim.
- id: incoming_sms
  description: Inbound text messages received on an account number.
  payload_fields: [id, received_at, text, from]
  note: The only inbound-from-the-outside-world event group; the rest are emissions from platform state changes.
gaps:
- No AsyncAPI, CloudEvents or JSON Schema document is published for any event group.
- No subscription API — endpoints are registered only through the manager UI.
- No event replay, no delivery log, and no dead-letter surface after the 4-hour retry window expires.
- No published event versioning; payload shapes are documented only as prose field tables.