Aller au contenu
ecdsa.com

Webhooks

Create a webhook endpoint

POSThttps://ecdsa.com/api/v1/webhooks

Creates a webhook endpoint for the API key's account. Creating one with a URL the account already has updates its description and event filter and switches it back on if it was switched off. The URL must be https on port 443 with a public host name; the host is resolved and refused when any of its addresses is private or reserved (webhook_url_refused). The number of endpoints is limited by the account's plan (webhook_limit_reached). Deliveries are POST requests with a JSON body, signed with ECDSA P-256 (ES256): headers ECDSA-Webhook-Id, ECDSA-Webhook-Timestamp and ECDSA-Signature (t=<unix seconds>,kid=<key id>,es256=<base64url r‖s>) over the UTF-8 bytes of "<id>.<t>.<raw body>". The public keys are the JWKS at https://ecdsa.com/.well-known/ecdsa-webhook-keys.json; every official SDK verifies deliveries with verifyWebhook().

Clé d’API
Obligatoire, avec le scope webhooks
Coût en quota avec une clé
1 unité par appel
Limite de requêtes
60 requêtes/min par IP

Requête

Corps JSON : CreateWebhookRequest

ChampTypeObligatoireDescription
urlstringoui

Where deliveries are POSTed: an https URL on port 443 with a public host name, at most 2048 characters.

descriptionstringnon

A note for yourself, at most 200 characters.

  • au plus 200 caractères
eventsstring[]non

Event types to receive ("monitor.grade_changed") or whole families ("monitor.*"); absent or empty receives every event. At most 20 entries.

  • au plus 20 éléments

Réponses

200The endpoint, created or updated.

ChampTypeObligatoireDescription
endpointWebhookEndpointoui
createdbooleanoui

false when the account already had an endpoint with this URL and it was updated.

En-têtes : X-Request-Id · X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset · X-Quota-Limit · X-Quota-Remaining · X-Quota-Reset

StatutSignificationCodes
400Bad request. Input limits: a request body of at most 100 KB (a larger one is answered with 413) and arrays and objects nested at most 64 levels deep, JSON documents inside string fields included.
401Unauthorized.
403Forbidden.
404Not found.
405Method not allowed.
413Payload too large.
429Too many requests.
500Internal server error.
503Service unavailable.
504Gateway timeout.

Chaque réponse d’erreur porte l’enveloppe JSON ; le tableau des erreurs indique ce que signifie chaque code et que faire.

Exemple

Receive every monitoring event

curl
curl -sS 'https://ecdsa.com/api/v1/webhooks' \
  -H "authorization: Bearer $ECDSA_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
  "url": "https://hooks.example.com/ecdsa",
  "description": "Production alerts",
  "events": [
    "monitor.*"
  ]
}'
JavaScript (fetch)
const response = await fetch("https://ecdsa.com/api/v1/webhooks", {
  method: "POST",
  headers: { "content-type": "application/json", authorization: `Bearer ${process.env.ECDSA_API_KEY}` },
  body: JSON.stringify({
    "url": "https://hooks.example.com/ecdsa",
    "description": "Production alerts",
    "events": [
      "monitor.*"
    ]
  }),
});
console.log(response.status, await response.json());
Python (httpx)
import os

import httpx

response = httpx.post(
    "https://ecdsa.com/api/v1/webhooks",
    headers={"authorization": f"Bearer {os.environ['ECDSA_API_KEY']}"},
    json={
        "url": "https://hooks.example.com/ecdsa",
        "description": "Production alerts",
        "events": [
            "monitor.*",
        ],
    },
    timeout=30.0,
)
print(response.status_code, response.json())
Réponse 200
{
  "endpoint": {
    "id": "7349e17c-8b41-4fc1-b1a9-dca88578ca1c",
    "url": "https://hooks.example.com/ecdsa",
    "description": "Production alerts",
    "events": [
      "monitor.*"
    ],
    "enabled": true,
    "createdAt": "2026-10-20T07:34:27.000Z",
    "disabledAt": null,
    "disabledReason": null,
    "lastDelivery": null
  },
  "created": true
}

La réponse de l’API à cet exemple, générée pour cette page le 20 octobre 2026 ; les temps relatifs et les nombres de jours qu’elle contient se rapportent à ce jour-là.

Codes d’erreur

CodeStatutSignification
invalid_json400The body is not valid JSON or not a JSON object; or the input nests arrays and objects more than 64 levels deep (JSON documents inside string fields count too), or is too complex to inspect for private keys.
missing_field400A required field is absent or empty.
invalid_input400A field could not be parsed — the message explains exactly why.
private_key_rejected400The request contains private-key material (a PEM PRIVATE KEY block, an OpenSSH, SSH2 or PuTTY private key, or a JWK with "d"). Never send private keys anywhere.
webhook_url_refused400The webhook URL cannot receive deliveries: it is not https on port 443, its host is not a public DNS name, or the name does not resolve or resolves (at least partly) to a private or reserved address.
unauthorized401The Authorization or X-API-Key header is malformed, the API key is not valid (wrong checksum, unknown, revoked or expired), or the operation needs a key.
forbidden_scope403The API key is valid but lacks a scope this operation requires; the message names it.
webhook_limit_reached403The account already has as many webhook endpoints as its plan allows.
not_found404No operation exists at this path.
method_not_allowed405The path exists but does not accept this HTTP method; see the Allow header.
payload_too_large413The body exceeds 100 KB.
rate_limited429Too many requests — see Retry-After.
quota_exceeded429A daily or monthly quota of the API key's account is used up. X-Quota-Reset (and Retry-After) say when it starts over; retrying earlier cannot succeed.
internal_error500Unexpected server error.
service_unavailable503API keys cannot be verified right now (the key store is unreachable); retry shortly.
webhooks_unavailable503Webhooks are switched off on this server: it has no signing keys configured.
timeout504The operation did not finish within its time budget.

Schémas

WebhookEndpoint

ChampTypeObligatoireDescription
idstringoui
  • format uuid
  • motif ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
urlstringoui
descriptionstring | nulloui
eventsstring[]oui

Subscribed event types or families ("monitor.*"); empty receives every event.

enabledbooleanoui
createdAtstringoui

UTC time, ISO 8601.

  • format date-time
disabledAtstring | nulloui
disabledReasonstring | nulloui

Why deliveries stopped: "consecutive_failures" after five failed deliveries in a row.

lastDeliveryWebhookDelivery | nulloui

WebhookDelivery

ChampTypeObligatoireDescription
idstringoui

Delivery id, sent as ECDSA-Webhook-Id and as the body's id; the same on every retry.

typestringoui

Event type, e.g. "monitor.grade_changed" or "webhook.test".

statusstringoui
  • au choix : "pending", "delivered" ou "failed"
attemptsintegeroui
  • au moins 0
createdAtstringoui

UTC time, ISO 8601.

  • format date-time
lastAttemptAtstring | nulloui
lastStatusCodeinteger | nulloui

HTTP status of the last attempt; null when no response arrived.

lastErrorstring | nulloui

Why the last attempt failed: "http_status" (any status outside 200–299, redirects included), "timeout", "address_refused", "tls_certificate", "endpoint_disabled", … ; null after a success.

nextAttemptAtstring | nulloui

When a pending delivery is tried again.

deliveredAtstring | nulloui

Error

The error envelope of every non-2xx response.

ChampTypeObligatoireDescription
errorobjectoui
error.codestringoui

Machine-readable error code. Each response lists the codes it can carry; new codes may be added within v1, so treat an unknown code by its HTTP status.

error.messagestringoui

Human-readable explanation (English).

error.requestIdstringnon

The X-Request-Id of this request — quote it when reporting a problem. Sent since v1.1.

error.detailobjectnon

Structured context for the error, such as the offending field; omitted when there is none.

  • noms de membres libres
error.docsstringnon

Where the error codes are explained. Sent since v1.1.

  • format uri

Toutes les opérations sont répertoriées dans la référence de l’API.