1. Webhook
InboxKit
  • Introduction
  • Order
    • Place Order
      POST
  • Webhook
    • Webhook Integration & Signature Verification
      POST
    • Event: Domain Status Changed
      POST
    • Event: Mailbox Status Changed
      POST
    • Events: OAuth Consent & Client ID Changes
      POST
  • Account
    • Get account details
      GET
  • Workspaces
    • List workspaces
      GET
    • Get workspace details
      GET
    • Create workspace
      POST
    • Update workspace
      PUT
    • Delete workspace
      DELETE
    • Update workspace webhook
      POST
  • Domains
    • Domain Renewals
    • Search available domains
      POST
    • Check if domain is banned from registration
      GET
    • Check domain availability
      GET
    • Register domains
      POST
    • List domains
      POST
    • List assignable domains
      GET
    • Remove domains
      POST
    • Set domain forwarding
      POST
    • Set catch-all email
      POST
    • Remove catch-all email
      DELETE
    • Get nameservers for domain connection
      POST
    • Check nameservers propagation
      POST
    • Check Workspace Availability
      POST
    • Set DMARC Configuration
      POST
    • Remove DMARC Configuration
      DELETE
    • Regenerate Domain Nameservers
      POST
    • Request Domain Renewal
      POST
    • Check Renewal Status
      GET
  • Cloudflare Domains
    • Connect Cloudflare Domains
    • List Connected Cloudflare Domains
    • Get Domain Details
    • Disconnect Cloudflare Domains
    • List Cloudflare Zones
  • Mailboxes
    • List mailboxes
    • Add or update signature for mailboxes
    • Delete signature for mailboxes
    • Show mailbox credentials
    • Get mailbox details
    • Buy mailboxes
    • Check mailbox status
    • Cancel mailboxes
    • Update mailbox
    • Update mailbox profile picture
    • Generate TOTP
    • Change username
    • Change password
    • Check mailbox availability
    • Get Failure Reason
    • Uncancel mailboxes
  • DNS
    • Get DNS records
    • Add DNS records
    • Update DNS records
    • Delete DNS records
  • Sequencers
    • Export mailboxes status
    • List sequencer platforms
    • Get sequencer tags
    • List sequencers
    • Export mailboxes
    • Add sequencer
    • Update sequencer
    • Delete sequencers
  • Billing
    • Get pricing plans
    • Get subscription details
    • Change subscription plan
    • Cancel subscription
    • Get wallet details
    • Add credits to wallet
    • Update auto top-up settings
    • Get billing portal link
  • Warmup
    • List Warmup Subscriptions
    • Get Warmup Subscription Details
    • Add Warmup to Mailboxes
    • Pause Warmup for Mailboxes
    • Resume Warmup for Mailboxes
    • Cancel Warmup for Mailboxes
    • Get Warmup Statistics
    • Get Warmup Pricing
  • Client ID Requests
    • Initiate Client ID Request
    • Get Client ID Request Status
    • List Client ID Requests
  • Mailbox Forwarding
    • Setup Email Forwarding
    • Update Email Forwarding
    • Remove Email Forwarding
    • List Forwarding Jobs
  • Consent Request
    • Initiate Consent Request
    • Get Consent Request Status
    • List Consent Requests
  • Tags
    • Assign tags to domains or mailboxes
    • Get all tags
    • Create a new tag
    • Delete a tag
  • Prewarm
    • Search Domains
    • Search Mailboxes
    • Buy Prewarmed Mailboxes
    • Check Order Status
    • Check Availability
  • Inbox Placement
    • Tests
      • Create Test
      • List Tests
      • Get Test Results
    • Scheduled Tests
      • List Scheduled Tests
      • Get Scheduled Test
      • Pause Scheduled Test
      • Resume Scheduled Test
      • Delete Scheduled Test
  • InfraGuard
    • InfraGuard - Subscriptions
      • List protected domains
      • Get subscription details
      • Enable InfraGuard for domains
      • Cancel InfraGuard for domains
      • Get workspace statistics
      • Get pricing info
    • InfraGuard - Monitoring
      • Get blacklist check history
      • Get DNS check history
      • Get current bounce metrics
      • Get bounce check history
    • InfraGuard - Settings
      • Update subscription settings
      • Trigger manual check
    • InfraGuard - Alerts
      • List alerts
      • Acknowledge an alert
      • Resolve an alert
    • InfraGuard - Action Items
      • List action items
      • Get action item statistics
      • Resolve an action item
      • Dismiss an action item
  • Email Insight
    • Email Insights - Workspace
      • Get Workspace Overview
      • Get Mailboxes Health
      • Get High Bounce Mailboxes
      • Get Workspace Activity
    • Email Insights - Mailbox
      • Get Mailbox Stats
      • Get Mailbox Deliverability
      • Get Mailbox Health
      • Get Mailbox Trends
      • Get Mailbox Activity
      • Get Mailbox Bounce Events
      • Get Mailbox Top Recipients
      • Get Mailbox Top Recipient Domains
      • Get Mailbox Message Breakdown
      • Get Mailbox Subject Analysis
  • Domain Renewals
  • Schemas
    • Schemas
      • WebhookPayload
      • DomainStatusWebhook
      • Tag
      • DomainEventData
      • MailboxStatusWebhook
      • MailboxEventData
      • Domain
      • Mailbox
      • ChangeDetails
      • WebhookResponse
      • ErrorResponse
  1. Webhook

Webhook Integration & Signature Verification

POST
https://api.inboxkit.com/webhook
Maintainer:Not configured

InboxKit Webhooks — Integration & Signature Verification#

Webhooks let InboxKit push real-time notifications to your server whenever important state changes happen in your account — domain status changes, mailbox provisioning milestones, OAuth consent results, and Google Client ID setup results.
You configure one URL in your InboxKit dashboard. InboxKit POSTs a JSON body to that URL every time something significant happens. Your server inspects the event field, does its thing, and returns 200 OK within 30 seconds.
šŸ“˜ New here? If you are integrating for the first time, start at the Quickstart. If your signature verification is failing, jump straight to šŸ” Signature Verification — that section has been updated to correct a previous documentation error.

The four events#

All four events arrive at the same webhook URL. Switch on the top-level event field to route them.
EventFires whenApplies toDedicated docs page
domain.status_changedA domain's lifecycle status transitions (purchased, activated, DNS propagating, cancelled, etc.)All domains, regardless of providerEvent: Domain Status Changed
mailbox.status_changedA mailbox's lifecycle status transitions (provisioning, activation, suspension, cancellation, etc.)Google Workspace and Microsoft 365 mailboxesEvent: Mailbox Status Changed
consent_request.status_changedAn OAuth consent request changes state (customer opens consent link, grants, denies, times out)Google Workspace & Microsoft 365 onboarding flowsEvents: OAuth Consent & Client ID Changes
client_id_request.status_changedA Google OAuth Client ID add/update request changes state in the customer's Workspace Admin ConsoleGoogle only (domain-wide delegation)Events: OAuth Consent & Client ID Changes

Quickstart#

1.
Open your InboxKit dashboard → Settings → API. This tab holds both your API Keys and Webhooks cards side-by-side.
2.
Under the Webhooks card, paste your endpoint URL (e.g. https://your-app.com/inboxkit/webhook). We strongly recommend an HTTPS endpoint — mailbox webhooks include plaintext credentials and HTTPS is what actually protects them in transit.
3.
Under the API Keys card, copy your team API key. You will pre-compute your expected signature from this value.
4.
Implement a POST handler on your endpoint. Accept application/json, parse the body, switch on event, respond with 200 OK within 30 seconds.
5.
Verify the X-InboxKit-Signature header on every request before trusting the payload. See šŸ” Signature Verification below.
A complete Node.js Express endpoint is provided further down this page.

Where InboxKit looks up your webhook URL#

InboxKit resolves the destination URL in this priority order:
1.
Team-level webhook URL (team.webhook_url) — the one on Settings → API → Webhooks. This is the normal place to set it.
2.
Workspace-level webhook URL (fallback) — if the team has no URL set, InboxKit falls back to the current workspace's webhook URL. This lets agencies / partners route by workspace.
3.
No webhook sent if neither is set.
For most integrations, set the team-level URL and ignore the fallback.

HTTP request shape#

InboxKit always delivers webhooks as:

Headers reference#

HeaderDescription
Content-TypeAlways application/json.
User-AgentAlways InboxKit-Webhook/1.0.
X-InboxKit-EventEvent type string. Use this to route without parsing the body.
X-InboxKit-TimestampISO 8601 UTC timestamp of when InboxKit generated the delivery.
X-InboxKit-Signaturesha256=<hex> — InboxKit sender-identity fingerprint. See Signature Verification section. Only sent if your team has an API key configured.

Shared envelope#

Every webhook body has this shape:
{
  "event": "<event name>",
  "timestamp": "<ISO 8601 UTC>",
  "team_id": "<your team uid>",
  "team_name": "<your team name>",
  "data": { "...event-specific fields..." }
}
The data object is different for each event — see the dedicated event pages in this section for the full field list.

Response contract#

Your endpoint must respond with any 2xx status code within 30 seconds. The body content is not inspected by InboxKit — an empty 200 OK is enough.
Pay close attention to the 4xx row — it is more nuanced than a blanket "4xx = no retry". Some 4xx codes are treated as permanent failures (no retry), others are retried just like 5xx.
Your responseInboxKit behavior
2xx (200–299)āœ… Success — delivery is marked complete, no retry.
3xx (300–399)Followed (up to 5 redirects), then evaluated on the final status.
400, 401, 403, 404, 405, 410, 451āŒ Permanent — no retry. These indicate your endpoint is fundamentally rejecting us (bad payload, auth wall, endpoint doesn't exist, method not allowed, gone, legally blocked).
Other 4xx (e.g. 408, 409, 429)šŸ”„ Retried up to 3 times. If you need InboxKit to slow down, return 429 Too Many Requests — it is not in the permanent set, so InboxKit will back off and retry.
5xx (500–599)šŸ”„ Retried up to 3 times with exponential backoff.
Timeout > 30sšŸ”„ Retried up to 3 times.
DNS lookup failed (ENOTFOUND), connection refused (ECONNREFUSED), invalid URLāŒ Permanent — no retry. These mean the URL itself is broken; retrying won't help.
Other network errors (ETIMEDOUT, ECONNRESET, EPIPE, EHOSTUNREACH, etc.)šŸ”„ Retried up to 3 times.

Idempotency#

Webhooks can be delivered more than once (e.g. if your endpoint returns 500 and we retry, but your original processing actually succeeded). You must be prepared for duplicates.
The recommended idempotency key is data.<entity>.uid — i.e. data.domain.uid, data.mailbox.uid, data.consent_request.uid, or data.client_id_request.uid. Combine it with event and the new status if you need to de-duplicate on a per-transition basis. InboxKit does not send a separate event_id header.

Retry policy & circuit breaker#

3 attempts per event (initial + 2 retries).
Exponential backoff: attempt n waits min(2^n seconds, 60 seconds) before retrying.
Retries ARE triggered by: 5xx responses, timeouts, most network errors, and 4xx codes other than the permanent-failure set below.
Retries are NOT triggered by: 2xx (success), the permanent HTTP codes (400, 401, 403, 404, 405, 410, 451), or the permanent network errors (ENOTFOUND, ECONNREFUSED, ERR_INVALID_URL). Permanent failures are marked as failed immediately, with no further attempts.
Practical tip: if you want InboxKit to slow down (e.g. your endpoint is being rate-limited upstream), respond with 429 Too Many Requests — it is not in the permanent set, so InboxKit will back off and retry.

Circuit breaker#

If your endpoint returns 5 consecutive failures, InboxKit's circuit breaker trips and temporarily stops sending webhooks to that URL. This protects both sides from hammering a broken endpoint.
Initial cooldown: 5 minutes.
If the next probe also fails, cooldown doubles each time, capped at 1 hour.
While the circuit is open, events that would have been delivered are held. When the cooldown expires, InboxKit sends a probe; if it succeeds, the circuit closes and held events are delivered normally.
What this means for you: if you break your endpoint, fix it, then wait a few minutes — InboxKit will automatically retry once the circuit half-opens.

šŸ” Signature Verification (X-InboxKit-Signature)#

āš ļø IMPORTANT — PLEASE READ EVEN IF YOU READ THE OLD DOCS. Older versions of this page described X-InboxKit-Signature as an "HMAC signature". That wording was misleading. It is not an HMAC. The signature is a plain SHA-256 hash of your team API key — the request body is not part of the hash. If you built verification using crypto.createHmac, it will never match. You need crypto.createHash('sha256'), as shown in the complete Node.js example below.

What the signature actually is#

The X-InboxKit-Signature header value is:
sha256=<lowercase hex SHA-256 of your team API key>
That is:
1.
Take your team API key (plaintext, as shown in InboxKit dashboard → Settings → API → API Keys).
2.
Compute SHA-256(api_key).
3.
Encode as lowercase hex.
4.
Prepend sha256=.
That's it. No HMAC. No payload is hashed. No per-request computation. The value is static per team — it only changes when you rotate your team API key.

What this signature proves (and doesn't prove)#

āœ… Proves: The caller possesses your team API key. Because only InboxKit and the team owner know the plaintext, a matching signature means the request came from InboxKit.
āŒ Does NOT prove: That the request body hasn't been tampered with. An attacker who intercepts a single signature can replay any body with it.

Defense-in-depth (strongly recommended)#

Because the signature doesn't cover the payload:
Use an HTTPS endpoint. TLS is what actually protects payload integrity in transit, and the mailbox event delivers plaintext credentials — you really don't want that over HTTP.
Validate every field before using it (types, lengths, enum values).
Rate-limit your webhook endpoint.
Log signature verification failures and alert on them — they are rare and usually indicate a misconfiguration or a probe.

Verification — step by step#

1.
Get your team API key from InboxKit dashboard → Settings → API → API Keys. Copy the whole value — watch for stray trailing newlines.
2.
Pre-compute the expected signature once at app startup (it does not change until you rotate the key):
expected = "sha256=" + lowercase_hex(sha256(api_key))
3.
On every incoming webhook request, read the X-InboxKit-Signature header and compare it to expected using a constant-time comparison. Reject with 401 if it does not match.

Complete Node.js (Express) example#

Sanity-check your setup from the terminal#

You can compute the expected signature value for your own API key with this one-liner — it produces exactly what InboxKit will send:
Or in bash:
Run either of these with your actual API key. Then compare the output to what you receive in the X-InboxKit-Signature header of a real webhook — they should match byte-for-byte.

Common signature mistakes (the checklist)#

These are the five issues we see most often in support:
1.
Using crypto.createHmac instead of crypto.createHash. This is the #1 mistake and it comes from the previous docs wording. There is no HMAC. Use createHash('sha256').
2.
Including a trailing newline in the API key when copy-pasting from the dashboard. "my-key\n" hashes to a completely different value than "my-key". Trim whitespace.
3.
Expecting uppercase hex. InboxKit emits lowercase hex. Compare lowercase-to-lowercase.
4.
Comparing with ===. This is a timing-attack surface. Use crypto.timingSafeEqual (Node), hmac.compare_digest (Python), hash_equals (PHP), subtle.ConstantTimeCompare (Go).
5.
Forgetting the sha256= prefix. The full header value includes the sha256= prefix. Don't strip it before comparing, or include it in both sides consistently.

What to do when you rotate your team API key#

When you rotate your team API key in the dashboard:
1.
New webhook jobs created after the rotation are signed with the new key's hash. In-flight jobs that were already queued before the rotation still carry the old key snapshot and will be signed with the old hash, so you may briefly see both old and new signatures arriving at your endpoint during a short transition window (usually seconds).
2.
Your application's cached EXPECTED_SIG will stop matching as soon as the first new-hash webhook arrives. You will see 401s in your logs until step 3 is done.
3.
Deploy the new INBOXKIT_API_KEY environment variable and restart (or SIGHUP / reload) so your app recomputes EXPECTED_SIG.
4.
Any webhooks your endpoint 401'd during the window will be retried up to 3 times by InboxKit's retry logic, so as long as you redeploy within the retry window, no data is lost.
If you need truly zero-downtime rotation, you can temporarily accept either the old or the new expected signature in your middleware for the length of the transition, then drop the old one.

Local testing without exposing your server#

During development you can send real InboxKit webhooks to a temporary public URL:
webhook.site — visit https://webhook.site, copy the unique URL, paste it into InboxKit's webhook settings, and you will see every request in real time in your browser. Great for inspecting the exact payload InboxKit sends.
ngrok — run ngrok http 3000 and paste the https://abc123.ngrok.io/inboxkit/webhook URL into InboxKit. Your local dev server will receive real webhooks.
RequestBin — similar to webhook.site.
Remember to switch the webhook URL back to your production URL when you're done.

Troubleshooting#

"I'm not receiving any webhooks at all"#

1.
Confirm the webhook URL is set in InboxKit dashboard → Settings → API → Webhooks.
2.
Confirm your server is reachable from the public internet and DNS resolves.
3.
If you set the URL at the team level, confirm the team has an API key set (otherwise the signature header will be missing, which is fine — but indicates an incomplete setup).
4.
Check your server logs — if the request is arriving but your middleware is rejecting it, you will see it.
5.
Check if the circuit breaker has tripped. If your endpoint returned 5+ consecutive errors, InboxKit is in cooldown — wait up to 1 hour for a probe.
6.
As a last resort, trigger a known event (e.g. create a new mailbox) and tail your logs.

"My signature always fails verification"#

Follow the Common signature mistakes checklist above. The #1 cause is using createHmac instead of createHash because the old docs said "HMAC".

"I'm getting duplicate webhooks for the same event"#

This is expected behavior if your endpoint ever returned non-2xx for the original delivery, or timed out. Use data.<entity>.uid as an idempotency key. Storing (event, entity_uid, new_status) tuples in a short-TTL cache is usually enough.

"InboxKit stopped sending webhooks after a deploy"#

Your endpoint probably returned 5xx for 5 consecutive requests during the deploy, tripping the circuit breaker. Fix the endpoint and wait for the cooldown to expire (5 minutes the first time, doubling up to 1 hour). InboxKit will automatically probe and resume once it passes.

"I'm receiving webhooks but the payload is missing fields I expect"#

Check the dedicated event page in this section (Domain / Mailbox / OAuth Events) — those pages list every field InboxKit sends, straight from the source code. Any field not listed there is not sent.

Where to go next#

Event: Domain Status Changed — full schema, all status values, 6 worked examples.
Event: Mailbox Status Changed — full schema, security notes about decrypted credentials, 6 worked examples.
Events: OAuth Consent & Client ID Changes — the two OAuth-onboarding events, combined because they share structure.

Request

Authorization
Provide your bearer token in the
Authorization
header when making requests to protected resources.
Example:
Authorization: Bearer ********************
Header Params

Body Params application/json

Examples

Responses

🟢200OK
application/json
Webhook received successfully. InboxKit will mark the delivery as complete and will not retry.
Body

🟠400Bad Request
🟠401Unauthorized
šŸ”“500Server Error
Request Request Example
Shell
JavaScript
Java
Swift
cURL
curl --location --request POST 'https://api.inboxkit.com/webhook' \
--header 'X-InboxKit-Signature: sha256=b0344c61d8db38e0f469e85fc6e8c055e0742e8d9a9c5a9e8e4d3f7c5a8b9c0d' \
--header 'X-InboxKit-Event: domain.status_changed' \
--header 'X-InboxKit-Timestamp: 2024-01-10T15:30:45.123Z' \
--header 'User-Agent: InboxKit-Webhook/1.0' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "event": "domain.status_changed",
    "timestamp": "2024-01-10T15:30:45.123Z",
    "team_id": "ABC-123-DEF-TEAM",
    "team_name": "Marketing Team",
    "data": {
        "domain": {
            "uid": "ABC-123-DEF-DOMAIN",
            "name": "yourdomain.com",
            "tld": "com",
            "status": "active",
            "previous_status": "pending_payment",
            "dns_propagation_status": "propagated",
            "nameservers": [
                "ns1.inboxkit.com",
                "ns2.inboxkit.com"
            ],
            "renewal_date": "2025-01-10T00:00:00.000Z",
            "renewal_status": "na",
            "registration_years": 1,
            "registration_date": "2024-01-10T00:00:00.000Z",
            "price": 12.99,
            "connection_type": "purchased",
            "forwarding_url": "",
            "forwarding_email": "",
            "forwarding_status": "",
            "enable_mask_forwarding": false,
            "dmarc_email": "",
            "catch_all_email": "",
            "assigned_mailboxes": 0,
            "available_mailboxes": 50,
            "tags": [
                "production"
            ]
        },
        "metadata": {
            "updated_at": "2024-01-10T15:30:45.123Z",
            "workspace_id": "ABC-123-DEF-WORKSPACE"
        }
    }
}'
Response Response Example
200 - Success
A minimal 200 response body is enough. The body content is not inspected by InboxKit — any 2xx status code counts as success.
{
  "received": true,
  "message": "Webhook processed successfully"
}
Modified atĀ 2026-04-10 19:43:00
Previous
Place Order
Next
Event: Domain Status Changed
Built with