Webhooks

Receive real-time updates from Taxbit as they happen: TIN matching results, tax form status changes, tax documentation updates, and inventory changes, pushed to an endpoint you control.

Overview

When something changes in your Taxbit organization, Taxbit sends it to you rather than making you poll for it. You give Taxbit an HTTPS endpoint and a list of event types; Taxbit sends a JSON payload in a POST to that endpoint each time a matching event occurs.

Every delivery is signed with a shared secret so you can confirm it came from Taxbit. Failed deliveries are retried, which means the same event can reach you more than once, so your handler needs to be idempotent. Both of those are covered in detail below.

👍

If you read one section

Read Responding to a delivery. The status code your endpoint returns has consequences beyond a single request: one class of response can disable your subscription until Taxbit manually re-enables it.

Receive your first event

Getting set up

Webhook subscriptions are configured by Taxbit, not self-serve. To get started, contact your implementation manager with the following.

  1. Your endpoint URL. Must be HTTPS. One subscription delivers all of its event types to a single endpoint; if you want different events routed to different endpoints, ask for separate subscriptions.
  2. The event types you want. Pick from the event catalog. You can add or remove types later, but it requires a configuration change on Taxbit's side; it is not instant.
  3. Whether you allowlist inbound IPs. If your endpoint sits behind an IP allowlist, say so up front. By default Taxbit's requests originate from cloud addresses that are not stable over time. Subscriptions can be configured to deliver from a fixed egress address instead; ask your implementation manager for the details.
  4. Whether your endpoint requires a credential to accept the request. Every delivery is signed regardless, but if a gateway in front of your endpoint needs HTTP authentication before it will route the request, say so. See Authentication.
  5. Optional delivery settings. If you have specific requirements, tell your implementation manager. See the defaults below.

Your implementation manager will then confirm the subscription and send you an HMAC secret through a secure channel. Store it as you would any other credential.

Delivery settings

Three settings are configurable per subscription. Unless you ask for something different, your subscription uses the defaults:

SettingDefaultWhat it controls
Rate limit300 req/secThe maximum rate at which Taxbit delivers to your endpoint. Lower it if your endpoint can't absorb bursts.
Retry attempts2How many times Taxbit re-attempts a failed delivery.
Retry window1 hourHow long Taxbit keeps retrying before giving up on an event.
🚧

Confirm your actual values

Many subscriptions are provisioned with values other than the defaults, commonly a lower rate limit and a much longer retry window. Ask your implementation manager to confirm the three numbers configured for your subscription in writing before you go live, and size your endpoint and your deduplication window against those, not against the defaults on this page.

Testing and going live

Before pointing a production endpoint at Taxbit, ask your implementation manager for a test subscription against a non-production endpoint of yours. Test in your sandbox covers testing the rest of your integration in your sandbox organization.

📘

How verification works

Taxbit does not send unsolicited probe traffic to customer endpoints, so there is no "send test event" button that fires at your server on demand. You verify from your side: trigger a real event in your test environment, then confirm it arrived in your own access logs.

  1. Stand up your endpoint in a non-production environment, with signature verification enabled and logging of x-trace-id on every request.
  2. Trigger an event in your Taxbit test environment that matches one of your subscribed types.
  3. Find the delivery in your logs. Confirm you received it, that your signature check passed, and that you returned 2xx.
  4. Test your failure path. Make your endpoint return 500 once and confirm you receive the retry. Confirm you never return 401, 403, or 407 on any code path, including from your WAF or gateway.
  5. Test idempotency. Replay a delivery you have already processed and confirm it does not double-apply.
  6. Go live. Give your implementation manager your production endpoint. Confirm your production rate limit and retry policy in writing at the same time. Go live covers the rest of the cutover.

Delivery

Anatomy of a delivery

A delivery is an HTTPS POST with a JSON body:

POST /your-webhook-endpoint HTTP/1.1
host: your-service.example.com
content-type: application/json
x-taxbit-signature: v1=<base64_hmac_signature>
x-taxbit-source: webhook
x-trace-id: <trace_id>

{
  "timestamp": "<timestamp>",
  "data": [
    {
      "event_type": "ACCOUNT_OWNER_TIN_VALIDATION",
      "account_owner_id": "<account_owner_id>",
      "account_owner_external_id": "<account_owner_external_id>",
      "status": "<tin_verification_status>",
      "validation_date": "<validation_date>"
    }
  ]
}

Headers

HeaderDescription
x-taxbit-signatureThe HMAC signature of the payload. This is how you authenticate the request; see Verifying the signature. May contain more than one signature.
x-trace-idAn opaque identifier for this delivery. Log it. It is the only value that lets Taxbit correlate your access logs with its own. Treat it as an arbitrary string: the format is not guaranteed and it is not always a UUID.
x-taxbit-sourceAlways the literal string webhook. This is a marker, not a credential. It is identical for every Taxbit customer and carries no secret. Do not use it to authenticate requests. Not sent if your subscription uses basic authentication; see Authentication.
authorizationOnly if your subscription is configured for basic authentication. Carries the credentials you supplied at onboarding. See Authentication.
content-typeapplication/json.

The payload envelope

  • timestamp: an ISO 8601 timestamp for when Taxbit dispatched this delivery, not when the underlying event occurred. Do not use it for business logic or ordering; use the date fields on the event itself, such as validation_date.
  • data: an array of event objects. Today this array always contains exactly one event. Iterate over it rather than reading data[0], so that if Taxbit begins batching events in the future it is not a breaking change for you.

Each object in data has an event_type field plus fields specific to that type. See the event catalog.

Responding to a delivery

Return 2xx once you have accepted the event. Taxbit retries 409, 429, 5xx, and timeouts according to your retry settings. Taxbit does not retry any other 4xx response, including 400 and 403: the event is not sent again after that attempt.

❗️

Never return 401 or 407

A 401 or 407 from your endpoint puts your subscription into a deauthorized state. This is not a temporary condition and it does not clear itself: delivery stops entirely, and it stays stopped even after you fix whatever caused the response. Restoring it requires manual action by Taxbit.

This applies to anything in front of your endpoint too: a WAF, an API gateway, a reverse proxy, or a rate limiter returning 401 or 407 on your behalf will do the same thing.

Avoid 403 as well. It does not deauthorize your subscription, but Taxbit does not retry it, so the event is not delivered again.

If you need to reject a request, including one whose signature does not verify, return 400 and alert your own team. A failed signature check means something is misconfigured, and you want a human looking at it, not your subscription switched off. A 400 is not retried, so once the cause is fixed, ask Taxbit Support to replay the affected events.

What to return, when

SituationReturnRetried
Accepted successfully200 to 299No
You are down, deploying, or overloaded500 to 599Yes
You are rate limiting Taxbit429, and tell Taxbit Support, so your subscription's rate limit can be lowered and Taxbit throttles at sourceYes
Signature invalid, or malformed payload400, and alert your team: this is a misconfigurationNo
Anything requiring authenticationNever 401 or 407: it permanently disables your subscription. Avoid 403: it is not retried—

Timeouts and retries

Your endpoint is expected to respond promptly, and a timeout counts as a failed delivery. Acknowledge the request as soon as you have durably queued the event, and do your processing afterwards rather than holding the connection open.

Retryable failures (timeouts, 409, 429, and 5xx) are retried up to your configured number of attempts, within your configured retry window. Once either limit is reached, Taxbit stops. See Delivery guarantees for what happens next.

Delivery guarantees

  • At least once. Retries mean you can receive the same event more than once, including after you have already returned 200 for it, for example if your response was lost in transit. Your handler must be idempotent. Ask Taxbit Support which field to deduplicate on for the event types you subscribe to.
  • No ordering guarantee. Events are delivered independently and can arrive out of order, including two events about the same account owner. Do not infer sequence from arrival order; use the event's own date fields, and be prepared to receive a stale event after a newer one.
  • One event per request. data currently always contains exactly one event. Write your handler to iterate the array so batching would not break you.
  • New fields and event types are added over time. Ignore fields you do not recognize rather than failing validation. A strict schema that rejects unknown fields will start dropping deliveries when a field is added.
  • After retries are exhausted, or after a response that is not retried. The event is retained rather than discarded, and Taxbit can replay it on request. Recovery is not automatic, and a replay can resend events you have already received, so rely on your deduplication: if you have had an outage, contact Taxbit Support promptly and give them the time range and, where you have them, the x-trace-id values.

Verification

Authentication

There are two independent layers to how a delivery is authenticated, and it is worth being clear about which does what.

1 · The payload signature, always on. Every delivery is signed with a secret shared only between you and Taxbit, and the signature travels in x-taxbit-signature. This is what proves a request genuinely came from Taxbit and was not modified in transit. It cannot be turned off, and it is the layer you should be checking.

2 · The connection credential, optional. A credential Taxbit presents to your endpoint so that infrastructure in front of your application (a gateway, a load balancer, an API manager) can admit the request before it ever reaches your code. This layer is about getting through your front door. It says nothing about whether the payload is authentic.

Layer 2 is configured per subscription. Whatever you choose there, layer 1 is unchanged.

Connection credentialWhat Taxbit sendsWhen to choose it
Source marker (default)x-taxbit-source: webhookThe default. A fixed marker, not a secret. Choose it when your endpoint is reachable directly and relies on the payload signature for authentication.
HTTP BasicAuthorization: Basic …Your endpoint sits behind something that requires HTTP authentication before it will route the request.

Basic authentication

Available on request. Give your implementation manager a username and password through the secure channel they provide, not over email or a support ticket. Once enabled, every delivery to your subscription carries a standard Authorization: Basic base64(username:password) header, and the x-taxbit-source header is no longer sent.

🚧

Basic auth adds to the signature; it does not replace it

You still receive x-taxbit-signature, and you should still verify it on every request. Basic credentials are replayable by anyone who captures a request and are only as strong as the TLS around them. The signature is what proves the payload came from Taxbit and arrived unmodified. Treat basic auth as a way through your gateway, not as your authentication.

❗️

Stale basic credentials are the fastest way to lose your subscription

If your endpoint rejects Taxbit's credentials with a 401 (because they were rotated on your side, expired, or were revoked), that response can put your subscription into the permanent deauthorized state described in Responding to a delivery. Delivery stops and does not resume when you fix the credential.

This has happened to real customers. If you use basic authentication, treat any change to those credentials as a coordinated change with Taxbit, not a routine rotation on your side.

Rotating basic credentials

Unlike the signing secret, a subscription holds one set of basic credentials: there is no overlap period where both old and new are accepted. Rotation is a cutover, so it has to be scheduled:

  1. Tell Taxbit Support you need to rotate, and agree a window.
  2. Configure your endpoint to accept both the old and new credentials.
  3. Taxbit updates the subscription to the new credentials.
  4. Confirm deliveries are arriving, then stop accepting the old credentials.

Step 2 is the one people skip. Without it there is a window where Taxbit is presenting credentials you no longer accept, and a 401 in that window can disable the subscription outright.

Verifying the signature

Every delivery carries an x-taxbit-signature header. Verify it on every request before you act on the payload. It is the only thing that distinguishes a genuine Taxbit delivery from anyone who has discovered your endpoint URL.

How the signature is built

  • The algorithm is HMAC-SHA256, keyed with the secret Taxbit gave you.
  • It is computed over the JSON payload: the same object you receive as the request body.
  • The digest is base64-encoded, not hex.
  • Each signature is prefixed with a version: v1=.
  • The header may contain multiple signatures, comma-separated. Accept the request if any one of them matches.

Example

This mirrors Taxbit's own implementation: parse the body, re-serialize it, and hash that.

const crypto = require('crypto')
const express = require('express')

const SECRET = process.env.TAXBIT_WEBHOOK_SECRET
const app = express()

// Returns true if any signature in the header matches, compared in constant time.
function isFromTaxbit (payload, header) {
  const expected = 'v1=' + crypto
    .createHmac('sha256', SECRET)
    .update(JSON.stringify(payload))
    .digest('base64')

  const expectedBuf = Buffer.from(expected)

  return (header || '')
    .split(',')
    .some((candidate) => {
      const buf = Buffer.from(candidate.trim())
      // timingSafeEqual throws on a length mismatch, so check length first.
      return buf.length === expectedBuf.length &&
             crypto.timingSafeEqual(buf, expectedBuf)
    })
}

app.post('/your-webhook-endpoint', express.json(), (req, res) => {
  if (!isFromTaxbit(req.body, req.get('x-taxbit-signature'))) {
    // Return 400, never 401 or 407. See "Responding to a delivery".
    return res.status(400).send('invalid signature')
  }

  for (const event of req.body.data) {
    enqueueForProcessing(event)   // do the real work off the request path
  }

  res.status(200).send('ok')
})
📘

Notes on the example

  • Compare signatures in constant time (crypto.timingSafeEqual above). A plain === or .includes() leaks information about the expected value.
  • Acknowledge fast and process asynchronously. Do the work after you have responded, not before.

Rotating your signing secret

To rotate, ask Taxbit Support to add a new secret. During the rotation your subscription is signed with both the old and the new secret, and x-taxbit-signature will contain two comma-separated signatures. Because you accept the request if any signature matches, you can switch your stored secret over at your own pace with no dropped deliveries. Once you confirm the new secret is in use, Taxbit removes the old one.

This only works if your verification loops over every signature in the header. Code that reads only the first signature, or string-matches the entire header value, will break the moment a rotation begins.

Troubleshooting

Whatever the symptom, have x-trace-id values and a time range ready. Without them, tracing a specific delivery is slow.

You have stopped receiving events entirely

Most often this is a deauthorized subscription. Work through, in order:

  • Did your endpoint return 401 or 407 at any point? Check your own logs, and check anything in front of your endpoint: a WAF rule, an expired certificate causing a gateway to reject Taxbit's requests, an IP allowlist that dropped an address. A single 401 or 407 can disable the subscription permanently. A 403 does not disable it, but each event that gets one is not retried.
  • Do you use basic authentication, and did those credentials change? A rotation, expiry, or revocation on your side means Taxbit starts presenting credentials you no longer accept, and the 401 that follows is the most common way a subscription gets disabled. See Rotating basic credentials.
  • Did anything else change on your side? A new firewall rule, a DNS change, a certificate renewal, a deploy that changed the route.
  • Was there genuinely traffic to send? Confirm the underlying events actually occurred in the window you're asking about.

Then contact Taxbit Support with the time you last received an event. A deauthorized subscription will not recover on its own, no matter how long you wait or how thoroughly you fix the original cause.

The signature does not verify

  • Are you hashing the right thing? The most common cause. See Verifying the signature.
  • Are you comparing against a base64 digest, not hex?
  • Are you checking every signature in the header? During a secret rotation there is more than one, and yours may not be first.
  • Are you including the v1= prefix in the value you compare?
  • Is your stored secret current? If a rotation completed and you never switched over, the old secret has been removed.

You are receiving duplicate events

Expected. Delivery is at-least-once; see Delivery guarantees. Deduplicate rather than treating it as a bug. If the volume is unusually high, check your endpoint's response times: a delivery you accept but acknowledge slowly can be retried before your response reaches Taxbit.

Events are arriving out of order

Also expected. There is no ordering guarantee. Use the date fields on each event and discard anything older than what you have already applied.

Taxbit is sending more traffic than you can handle

Return 429 and contact Taxbit Support to have your subscription's rate limit lowered. Throttling at Taxbit's end is better than absorbing and shedding at yours.


For the errors the Taxbit API returns to your own requests, see Errors and rate limits.

Questions about your subscription (endpoint changes, new event types, secret rotation, rate limits, or recovering missed events) go to Taxbit Support or [email protected].