Skip to main content
Webhooks let your application receive an HTTP callback when HydraDB finishes processing a document or memory. Use them when you want to:
  • Update your own database when content is ready for query
  • Notify users that an upload has finished
  • Trigger downstream jobs after indexing completes
  • Track indexing failures without polling
Webhooks are sent for terminal indexing states. For progress updates before completion, use Ingestion Status.

1. How it works

When an ingested item reaches a terminal state, HydraDB creates a delivery record and sends a POST request to your webhook URL. The supported event today is: success is a legacy alias for completed.

2. Register a webhook

Open the HydraDB dashboard and go to Webhooks.
  1. Click Register Webhook.
  2. Enter your public HTTPS endpoint.
  3. Select indexing.status_changed.
  4. Save the webhook.
  5. Enable signing and copy the generated secret. It is shown once and cannot be retrieved later.
  6. Use Send Test to confirm your endpoint receives a request.
Signing is optional but strongly recommended: without it, your endpoint cannot tell a real HydraDB delivery from anything else that can reach the URL. See Verifying signatures.
Your webhook URL must be reachable from the public internet. Localhost and private network addresses are blocked.
Webhook management endpoints return their response object directly, not inside the standard v2 { success, data, error, meta } envelope used by /databases, /context/*, and /query.

Register with cURL

Use your HydraDB API key to register a webhook from your backend or terminal.
cURL
Response:
signing_secret is optional here and must be at least 16 characters if you supply one. To have HydraDB generate a strong secret instead, send generate_signing_secret and register in a single call. The secret comes back once on the response, so registering and enabling signing are one atomic step:
cURL
Response:
generate_signing_secret and signing_secret are mutually exclusive; sending both returns 422. Whichever you choose, your receiver should verify X-HydraDB-Signature on every delivery.

Check the current registration

cURL
Response:

Edit a webhook

Use the same POST /webhooks/indexing endpoint to replace the existing registration.
cURL
Omitting signing_secret while editing preserves the secret you already have. Editing the URL or the event list never changes your signing configuration. To turn signing off, call DELETE /webhooks/indexing/signing-secret explicitly.

Manage the signing secret

The signing secret has its own endpoint, so enabling, rotating, and disabling are always deliberate actions rather than side effects of editing a registration. Generate a secret. Send no body and HydraDB creates a strong one for you. The secret is returned exactly once, in plain text.
cURL
Response:
Supply your own. Useful when you want to deploy the secret to your receiver first and hand it to HydraDB afterwards, so there is no window where deliveries are signed with a secret your endpoint does not yet know.
cURL
Disable signing. This removes the stored secret, and deliveries stop carrying X-HydraDB-Signature.
cURL
Rotating takes effect immediately, with no overlap period. Deliveries in flight during a rotation are signed with the new secret, so a receiver that only knows the old one will reject them. See Zero-downtime key rotation for how to rotate without a gap.

Send a test delivery

cURL
Response:

Delete a webhook

cURL
Response:

3. Request format

HydraDB sends a POST request with a JSON body.

Headers

The signature scheme in full:
Three details decide whether your verifier works:
  • The sha256= prefix is part of the header value, not a separate field. Compare against the whole string.
  • The digest is lowercase hex, not base64.
  • The HMAC is computed over the raw body bytes exactly as received. Parsing the JSON and re-serialising it produces different bytes and the signature will not match.

Payload

For failed indexing, the payload can include error_code and error_message:
Older examples may refer to this document identifier as doc_id. New webhook payloads use id.
tenant_id and database do not carry the same value. database is the name you ingested into (marketing-docs); tenant_id is an identifier for it (kv3qz7mabx). Route and filter on database - it is the only field that matches what you sent.tenant_id still carries the same identifier it always has, so integrations matching on it keep working unchanged. sub_tenant_id remains an exact alias for collection. Read database and collection in new integrations.
database is empty only for items ingested before this field existed. Read tenant_id if you need a scope that is always set.

4. Test delivery payload

The dashboard Send Test button sends a synthetic event. It does not create a real indexing delivery.
Nothing was ingested, so there is no database name to report: the test payload sets every scope field - including database - to your organisation ID. A real delivery reports the database you ingested into. Match on test: true (or the test_ prefix on delivery_id) to tell the two apart.
Use test: true to ignore test events in production workflows.

5. Verifying signatures

Verification is one function. It takes your signing secret, the raw request body, and the X-HydraDB-Signature header, and returns whether the delivery genuinely came from HydraDB. If you use an official SDK, it is already there and you can skip the rest of this section:
Otherwise, implement it directly. Both use a constant-time comparison, which matters: a naive == leaks timing information that can be used to forge a signature byte by byte.
Fail closed. If the signing secret is missing from your environment, reject the request rather than skipping verification. A verifier that silently accepts everything when a variable is unset is worse than no verifier, because it looks like it is protecting you.

6. Receiver examples

Your endpoint should return a 2xx response quickly. Do any slow work after you acknowledge the request. These wire the verifier above into a real handler. Note that both read the raw body before parsing.

7. Delivery and retries

HydraDB records every delivery attempt. You can inspect delivery history from the dashboard Webhooks page. Delivery states: HydraDB retries failed deliveries in the background. If a worker shuts down during delivery, the sweep process recovers the event later. Your receiver should be idempotent:
  • Store delivery_id.
  • If the same delivery_id arrives again, return 2xx without repeating side effects.
  • Do not depend on receiving events exactly once.

List deliveries with cURL

cURL
Response:
Delivery history uses doc_id internally. The outbound webhook payload uses id.

Filter deliveries

cURL
Valid filters are:
  • pending
  • sweeping
  • delivered
  • failed
  • permanently_failed

Paginate deliveries

When next_cursor is not null, pass it back as cursor.
cURL
limit can be between 1 and 100.

Get one delivery

Replace <delivery_id> with a value from a webhook payload or from the delivery list response.
cURL

Retry a failed delivery

Only failed and permanently_failed deliveries can be retried manually.
cURL
Response:

8. Advanced patterns

Zero-downtime key rotation

Rotating takes effect immediately and HydraDB signs each delivery with exactly one secret, so a naive rotation leaves a window where deliveries are signed with a secret your receiver does not have yet. Every delivery in that window fails verification, and a correct receiver rejects them. The fix is to make the change on your side first, and overlap the two secrets in your own receiver. That requires knowing the new secret before HydraDB starts using it, which is why you supply it yourself rather than having one generated.
1

Choose the new secret yourself

Generate a high-entropy value with your own tooling, for example openssl rand -base64 32. Do not use the generate option here: a generated secret is only revealed after it is already in effect, which is exactly the window you are trying to avoid.
2

Teach your receiver both secrets

Deploy your receiver so it accepts either the current secret or the new one, reading both from your environment. At this point nothing has changed on the HydraDB side, so every delivery still verifies against the old secret.
3

Rotate in HydraDB, supplying that secret

In the dashboard, open Rotate, tick I’ll use my own secret, and paste the value. Over the API, POST /webhooks/indexing/signing-secret with a signing_secret body. Deliveries switch to the new secret immediately, and your receiver already accepts it.
4

Retire the old secret

Once you have confirmed deliveries are verifying against the new secret, remove the old one from your receiver and redeploy. You are back to a single active secret.
A receiver that accepts either secret:
The overlap lives in your receiver, not in HydraDB. Each delivery carries a single X-HydraDB-Signature value, so accepting two secrets is something your code does during the changeover. Keep the window short and remove the old secret once the rotation is confirmed.

9. Security checklist

  • Use HTTPS for your webhook URL.
  • Enable signing and let HydraDB generate the secret. A secret you invent yourself is usually far weaker than one from a CSPRNG, and a weak secret makes the signature decorative.
  • Verify X-HydraDB-Signature using the raw request body, with a constant-time comparison.
  • Fail closed. Reject the request when the secret is missing from your environment, rather than skipping verification.
  • Store the secret as a secret. It belongs in your secret manager, not in source control.
  • Return 2xx only after you accept the event.
  • Deduplicate using delivery_id.
  • Keep the endpoint fast. Put slow work in a queue or background job.

10. Common issues