Skip to main content

Configure a webhook

Under Webhooks in the dashboard, confirm account availability, register your server’s HTTPS URL, select rates and events, and store the whsec_… secret in private configuration. It differs from your API key. Deliveries do not consume API requests. The schema described here uses the simplified rate notification: delivery_type identifies tests and previous_mid is the previous value. Site language does not change the payload or signature.

Plans and limits

Gratis and Inicio do not include webhooks. Paused endpoints count towards the limit; the test webhook does not. The notification cap resets on the first day of each UTC month, separately from the API-request cycle. Rate changes and account events count; retries, tests, sync, recovery and probe do not. Exhaustion blocks Binance, including its synchronisation and recovery; BCV, parallel and account events continue. The dashboard warns at 80%, 95% and 100% of the notification cap. Moving to a plan without webhooks pauses them without deleting them. If the new limit is lower, older endpoints take priority. Returning to a compatible plan can reactivate endpoints paused by plan changes; new subscriptions are not selected automatically.

Markets and frequency

The four markets are reference (USD), eur_reference (EUR), parallel (USD) and binance (USD). Other markets are queried through the API. BCV and parallel notify on detected changes; Binance honours the plan interval and sends the most recent changed value. Not every intermediate movement is sent, and an exact delivery time is not guaranteed.

Synchronisation

Creating, reactivating or changing the URL, rates or headers sends the current value, at most once per market and webhook every 15 minutes. Editing only the description does not trigger synchronisation. An exhausted cap still blocks Binance.

Envelope and data

The envelope contains id, type, timestamp, delivery_type and data. timestamp identifies the event and stays unchanged on retries. The current schema does not include the test boolean: identify tests with delivery_type === "test". Illustrative response: figures and dates are not current quotes.
rate.updated requires market, mid and updated_at. ask, bid, previous_mid and change_pct are omitted when unavailable. effective_date is present for BCV. change_pct compares with the previous market value, not necessarily the last value you received. type, base, best, vs_reference_pct, source_changed and the previous object are no longer part of data; the base of eur_reference is EUR, and the other three use USD. Plans in JSON use codes such as free, lite, hobby, indie and pro. Do not translate values or fields when switching languages.

Delivery type

Process known types according to your application. Trigger movement alerts only for change; sync, probe and recovery may repeat the same value. Record an unknown or missing type for review; do not automatically treat it as a change.

Headers

HTTP header names are case-insensitive. cotizave-* headers are not signed: select the secret from trusted configuration, then verify. Make decisions using type and delivery_type from the verified body.

Verify, persist and respond

Return 2xx within 10 seconds after verifying and durably storing the event. If persistence fails, return 503 to allow a retry. Do not return 200 before storage or use an in-memory-only queue. This Node.js adapter uses the verifier in the next section. Implement secretFor with your private secrets and persistOnce with a transaction and a unique constraint per webhook and event. An already-persisted duplicate must resolve successfully; an actual storage failure must reject the promise. Configure express.raw for this route before any JSON parser and limit body size.
webhook-handler.mjs
A worker processes pending records and marks their state transactionally. Do not mark an event processed before completing its effect. For rates, avoid overwriting a newer updated_at by comparing timestamps and coordinating concurrent updates. Signature verification does not replace deduplication.

HMAC-SHA256 signature

The signed bytes are webhook-id + "." + webhook-timestamp + "." + original body. The key is the secret without whsec_, decoded from base64. Do not reserialise JSON or translate content before verification. Reject a clock difference greater than 300 seconds and compare signatures in constant time. Accept any valid signature during rotation. The format follows Standard Webhooks.
Store the secret on the server, with a separate secret per webhook. The dashboard allows reveal or rotation after a recent login (less than one hour). Previous-secret overlap can be 0, 1h, 24h or 7d; with 0, it stops signing. During overlap, two signatures are sent. Each receiver can use its valid secret within that window.

Local test vector

This secret is public and for tests only. Use the indicated timestamp as now in tests. Body bytes must match exactly; changing content, whitespace or accents invalidates the signature.
signature-vector.json

Retries and pausing

2xx confirms delivery. Network errors, 408, 429 and 5xx may be retried up to four attempts: immediately, then after 10 s, 30 s and 2 min, with up to ±20% variation. For 429/5xx, Retry-After can extend the wait up to 2 min; a longer value stops retries for that notification. Other 4xx and 3xx redirects fail without retry. 410 immediately pauses a live webhook. The ID and body stay unchanged between attempts; the send timestamp and signature are refreshed. A newer rate notification may replace a pending one, which can no longer be resent. After a failure, current-value probes run every 5 min; on recovery, missing rates are synchronised. Account events do not generate probes. At most one delivery per rate and webhook is in flight; there is no ordering across rates. Six consecutive failures and at least 10 min failing pause the webhook; a 2xx resets the count. Reactivate it in the dashboard after fixing your receiver, subject to synchronisation limits. Tests do not trigger automatic pausing.

URL and custom headers

The URL must use HTTPS and port 443, with a domain resolving to a public IP, no credentials or fragment, and at most 2,048 characters. Literal IPs, localhost, internal/local domains and Cotizave domains are not allowed. Register the final URL: redirects are not followed. You can configure up to three headers, with valid HTTP names up to 64 characters and visible ASCII values up to 1 KB. Transport and security headers are reserved, including host, content-type, content-length, user-agent, cookies and the prefixes webhook-, cotizave-, proxy- and cf-. The dashboard validates names. A custom token does not replace HMAC.

Test webhook

The test webhook only receives requested tests or deliveries forwarded to the test environment, with delivery_type: test. It has its own secret and history; it does not consume the notification cap or automatically receive live changes. A test resend gets a new ID. A rate test uses a current quote; if unavailable, it returns 409 quote_unavailable without sending.

Validate the integration

First run local tests with the vector: valid signature, altered body, expired timestamp, multiple signatures and duplicates. For a real test, use your public HTTPS test endpoint and the dashboard button. Check delivery history within its retention window and retain your own business records. Do not log secrets or the full signature header.