- 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 aPOST 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.- Click Register Webhook.
- Enter your public HTTPS endpoint.
- Select
indexing.status_changed. - Save the webhook.
- Enable signing and copy the generated secret. It is shown once and cannot be retrieved later.
- Use Send Test to confirm your endpoint receives a request.
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
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
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
Edit a webhook
Use the samePOST /webhooks/indexing endpoint to replace the existing registration.
cURL
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
cURL
X-HydraDB-Signature.
cURL
Send a test delivery
cURL
Delete a webhook
cURL
3. Request format
HydraDB sends aPOST request with a JSON body.
Headers
The signature scheme in full:
- 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
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.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 theX-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:
== leaks timing information that can be used to forge a signature byte by byte.
6. Receiver examples
Your endpoint should return a2xx 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_idarrives again, return2xxwithout repeating side effects. - Do not depend on receiving events exactly once.
List deliveries with cURL
cURL
doc_id internally. The outbound webhook payload uses id.
Filter deliveries
cURL
pendingsweepingdeliveredfailedpermanently_failed
Paginate deliveries
Whennext_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
Onlyfailed and permanently_failed deliveries can be retried manually.
cURL
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.
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-Signatureusing 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
2xxonly after you accept the event. - Deduplicate using
delivery_id. - Keep the endpoint fast. Put slow work in a queue or background job.
