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 sectionRead 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.
- 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.
- 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.
- 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.
- 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.
- 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:
| Setting | Default | What it controls |
|---|---|---|
| Rate limit | 300 req/sec | The maximum rate at which Taxbit delivers to your endpoint. Lower it if your endpoint can't absorb bursts. |
| Retry attempts | 2 | How many times Taxbit re-attempts a failed delivery. |
| Retry window | 1 hour | How long Taxbit keeps retrying before giving up on an event. |
Confirm your actual valuesMany 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 worksTaxbit 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.
- Stand up your endpoint in a non-production environment, with signature verification enabled and logging of
x-trace-idon every request. - Trigger an event in your Taxbit test environment that matches one of your subscribed types.
- Find the delivery in your logs. Confirm you received it, that your signature check passed, and that you returned
2xx. - Test your failure path. Make your endpoint return
500once and confirm you receive the retry. Confirm you never return401,403, or407on any code path, including from your WAF or gateway. - Test idempotency. Replay a delivery you have already processed and confirm it does not double-apply.
- 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
| Header | Description |
|---|---|
x-taxbit-signature | The HMAC signature of the payload. This is how you authenticate the request; see Verifying the signature. May contain more than one signature. |
x-trace-id | An 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-source | Always 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. |
authorization | Only if your subscription is configured for basic authentication. Carries the credentials you supplied at onboarding. See Authentication. |
content-type | application/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 asvalidation_date.data: an array of event objects. Today this array always contains exactly one event. Iterate over it rather than readingdata[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 407A
401or407from 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
401or407on your behalf will do the same thing.Avoid
403as 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
400and 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. A400is not retried, so once the cause is fixed, ask Taxbit Support to replay the affected events.
What to return, when
| Situation | Return | Retried |
|---|---|---|
| Accepted successfully | 200 to 299 | No |
| You are down, deploying, or overloaded | 500 to 599 | Yes |
| You are rate limiting Taxbit | 429, and tell Taxbit Support, so your subscription's rate limit can be lowered and Taxbit throttles at source | Yes |
| Signature invalid, or malformed payload | 400, and alert your team: this is a misconfiguration | No |
| Anything requiring authentication | Never 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
200for 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.
datacurrently 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-idvalues.
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 credential | What Taxbit sends | When to choose it |
|---|---|---|
| Source marker (default) | x-taxbit-source: webhook | The default. A fixed marker, not a secret. Choose it when your endpoint is reachable directly and relies on the payload signature for authentication. |
| HTTP Basic | Authorization: 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 itYou 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 subscriptionIf 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:
- Tell Taxbit Support you need to rotate, and agree a window.
- Configure your endpoint to accept both the old and new credentials.
- Taxbit updates the subscription to the new credentials.
- 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.timingSafeEqualabove). 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
401or407at 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 single401or407can disable the subscription permanently. A403does 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
401that 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].
Updated 5 days ago
