Outbound Webhooks Technical Details
Ad Orbit can POST a signed JSON payload to a configured HTTPS endpoint whenever a Company or Contact record is created or updated. Webhooks give your external systems real-time visibility into CRM changes without polling the Ad Orbit API.
Each webhook targets one event type and one destination URL. Configure webhooks under Settings → Integrations → Webhooks.
Note
Webhooks are not fired for records brought in through bulk imports.
Four event types are available:
company.created: A new Company is created
company.updated: An existing Company is saved
contact.created: A new Contact is created
contact.updated: An existing Contact is saved
contact.qualified: A Contact is updated and matches one of the configured lead statuses. When both contact.qualified and contact.updated are active, contact.updated will not fire for events where contact.qualified fires.
Every delivery is an HTTP POST with a JSON body. The table below summarizes the request settings:
Setting | Value |
|---|---|
Method | POST |
Content-Type | application/json |
Success | Any 2xx response (200-299) |
Connect timeout | 5 seconds |
Total timeout | 15 seconds |
Request Headers
Ad Orbit follows the Standard Webhooks header convention. The following headers are included with every delivery:
Header | Example Value | Description |
|---|---|---|
Content-Type | application/json | JSON content marker |
webhook-id | 87654 | Unique delivery ID - use as an idempotency key |
webhook-timestamp | 1714000200 | Unix timestamp at signing time; reject if older than 5 minutes |
webhook-type | company.updated | Event type - lets your receiver route without parsing the body |
webhook-signature | v1,abc123... | HMAC-SHA256 signature - see Signature Verification |
Every delivery body follows this envelope structure:

The webhook_id field mirrors the webhook-id header and is included in the body so you can deduplicate deliveries even if your receiver strips headers. Timestamps are ISO 8601 in UTC (e.g., 2026-04-22T14:30:00Z).
Company Payload Fields
The data object contains the following fields for company events:
Field | Type | Description |
|---|---|---|
id | integer | Ad Orbit company ID |
name | string | null | Company name |
xref | string | null | Your external ID for this company |
type | string | null | Company type |
active | boolean | false when the company has been deactivated |
phone_primary | string | null | Primary phone number |
phone_primary_ext | string | null | Phone extension |
address | string | null | Street address line 1 |
address2 | string | null | Street address line 2 |
city | string | null | City |
county | string | null | County |
state | string | null | State / province |
zip | string | null | Postal code |
country | string | null | Country |
website | string | null | Website URL |
market | string | null | Market |
primary_rep_id | integer | ID of the assigned rep (0 when unassigned) |
primary_rep_firstname | string | null | Rep first name |
primary_rep_lastname | string | null | Rep last name |
primary_rep_email | string | null | Rep email address |
linkedin_url | string | null | LinkedIn profile URL |
x_url | string | null | X (Twitter) URL |
facebook_url | string | null | Facebook URL |
currency | string | null | Currency code |
publisher_name | string | null | Publisher name |
categories | string[] | Category names |
market_territories | string[] | Market territory names |
dynamic_attributes | object[] | Custom fields - each item: {id, name, value} |
created_at | string | null | ISO 8601 UTC creation timestamp |
updated_at | string | null | ISO 8601 UTC last-updated timestamp |
revenue_all_time | number | null | Total revenue from this company across all time |
revenue_this_year | number | null | Revenue from this company in the current calendar year |
revenue_print_ads | number | null | Revenue attributed to print ad orders |
revenue_digital | number | null | Revenue attributed to digital orders |
revenue_digital_media | number | null | Revenue attributed to digital media orders |
revenue_service_products | number | null | Revenue attributed to service orders |
revenue_events | number | null | Revenue attributed to event orders |
revenue_delivery_solutions | number | null | Revenue attributed to delivery solution orders |
recent_order_date | string | null | ISO 8601 UTC date of the most recent order |
recent_order_amount | number | null | Amount of th emost recent order |
last_activity | string | null | ISO 8601 UTC timestamp of the most recent activity |
last_note | string | null | ISO 8601 UTC timestamp of the most recent note |
last_meeting | string | null | ISO 8601 UTC timestamp of the most recent meeting |
last_call | string | null | ISO 8601 UTC timestamp of the most recent call |
last_email | string | null | ISO 8601 UTC timestamp of the most recent email |
advertised_publications | string[] | Names of publications this company has advertised in |
Contact Payload Fields
The data object contains the following fields for contact events:
Field | Type | Description |
|---|---|---|
id | integer | Ad Orbit contact ID |
xref | string | null | Your external ID for this contact |
firstname | string | null | First name |
middlename | string | null | Middle name |
lastname | string | null | Last name |
string | null | Primary email address | |
company_id | integer | ID of the associated company |
company_name | string | null | Company name |
job_title | string | null | Job title |
department | string | null | Department |
phone_cell | string | null | Primary phone number |
phone_cell_ext | string | null | Phone extension |
email_home | string | null | Home email address |
email_other | string | null | Other email address |
address | string | null | Street address line 1 |
address2 | string | null | Street address line 2 |
city | string | null | City |
county | string | null | County |
state | string | null | State |
zip | string | null | Postal code |
country | string | null | Country |
owner_id | integer | null | ID of the assigned owner |
owner_firstname | string | null | Owner first name |
owner_lastname | string | null | Owner last name |
owner_email | string | null | Owner email address |
lead_source_id | integer | null | Lead source ID |
lead_source | string | null | Lead source name |
lead_status_id | integer | null | Lead status ID |
lead_status | string | null | Lead status name |
active | boolean | false when the contact has been deactivated |
primary_contact | boolean | Whether this is the primary contact for the company |
dynamic_attributes | object[] | Custom fields - each item: {id, name, value} |
created_at | string | null | ISO 8601 UTC creation timestamp |
updated_at | string | null | ISO 8601 UTC last-updated timestamp |
salutation_id | integer | null | ID of the contact's salutation |
salutation | string | null | Salutation label (e.g., Mr., Ms., Dr.) |
gender_id | integer | null | ID of the contact's gender |
gender | string | null | Gender label |
fax | string | null | Fax number |
linkedin_url | string | null | LinkedIn profile URL |
x_url | string | null | X (Twitter) profile URL |
facebook_url | string | null | Facebook profile URL |
notes | string | null | Free-text notes on the contact |
client_center_username | string | null | Username for Client Center login |
last_activity | string | null | ISO 8601 UTC timestamp of the most recent activity |
last_note | string | null | ISO 8601 UTC timestamp of the most recent note |
last_meeting | string | null | ISO 8601 UTC timestamp of the most recent meeting |
last_call | string | null | ISO 8601 UTC timestamp of the most recent call |
last_email | string | null | ISO 8601 UTC timestamp of the most recent email |
Ad Orbit signs every delivery using the Standard Webhooks format. You can verify signatures using any Standard Webhooks-compatible library, or manually using the steps below.
Secret Format
Your webhook secret is displayed in whsec_<base64> format when you create or regenerate it. Store the full string immediately — it is never shown again after you leave the page.
Configuring an HMAC secret key is optional. If no secret is configured, the webhook-signature header will not be included in deliveries. For most integrations, configuring a secret is strongly recommended.
How the Signature is Computed
signed_content = webhook-id + "." + webhook-timestamp + "." + raw_request_body signature = "v1," + base64( hmac-sha256( signed_content, base64_decode( secret_after_whsec_prefix ) ) )
The webhook-signature header may contain multiple space-separated v1,... values when a secret has been rotated. A delivery is valid if any value matches.
Verification Steps
Read webhook-id, webhook-timestamp, and webhook-signature from the request headers.
Reject if webhook-timestamp is more than 5 minutes old (replay protection).
Decode the key: strip the whsec_ prefix from your secret, then base64-decode the remainder.
Build signed_content = webhook-id + "." + webhook-timestamp + "." + raw_body.
Compute expected = "v1," + base64( hmac-sha256( signed_content, decoded_key ) ).
Compare expected against each value in webhook-signature using a constant-time comparison. Accept if any matches.
Example (PHP)

Ad Orbit attempts each delivery up to three times:
Attempt 1: Immediately after the event
Attempt 2: ~1 hour after attempt 1
Attempt 3: ~1 hour after attempt 2
Any 2xx response is treated as success. A non-2xx response or connection error triggers a retry. After all three attempts fail, the delivery is marked permanently failed.
A delivery may also be marked Canceled. This occurs when a pending or retrying delivery is superseded by a newer event for the same entity. When a record is updated while a previous webhook for that record is still awaiting retry, the original delivery is automatically canceled and the newer event takes over. No manual action is required when a delivery shows Canceled — the replacement event will deliver the most current data.
Failed deliveries can be retried manually from Settings → Integrations → Webhook Logs.
Deduplication
If a record is saved but nothing in the payload has changed — for example, an unrelated internal field was touched — Ad Orbit will skip the delivery rather than sending a duplicate.
If a webhook accumulates 10 consecutive failed deliveries, it is automatically disabled.
To re-enable: go to Settings → Integrations → Webhooks and toggle the webhook back on. This also resets the failure count. Any failed deliveries must be retried manually after re-enabling.
The destination URL must meet the following criteria:
Must be publicly reachable over HTTPS.
Must not resolve to a private or internal network address (Ad Orbit rejects such URLs).
If your endpoint is behind a firewall, ensure it accepts inbound HTTPS traffic from external sources.
Delivery logs are kept for 140 days and then pruned automatically. Review recent delivery history under Settings → Integrations → Webhook Logs.
New 6/4/2026