Skip to content

Endpoints API

Every route on this page requires a management API key. See Authentication.

For what these routes mean, including secret rotation and active-state behaviour, see Endpoint management.

POST /endpoints

Registers a form endpoint.

Request body

Field Required Type Meaning
id yes string The public identifier the endpoint answers on
name yes string Human-readable label, 1 to 200 characters
is_active no boolean Whether it accepts submissions, defaults to true
webhook_url no string Where accepted submissions are delivered

id is 3 to 64 characters of lowercase ASCII letters, digits, - and _, and must start and end with a letter or digit.

Responses

Status Code Cause
201 Created
401 authentication_required, invalid_api_key Credential missing or unusable
409 endpoint_already_exists The ID is already taken
422 invalid_endpoint_id The ID breaks the format rules
422 invalid_webhook_url The destination is malformed or not permitted
422 invalid_request The body failed schema validation
503 storage_unavailable The database could not be reached

webhook_secret is present in the 201 body only when a secret was generated. It is never returned again.

GET /endpoints

Lists endpoints, newest first.

Query parameters

Parameter Default Meaning
limit 50 Page size, 1 to 100
cursor none The previous page's next_cursor

Responses

Status Code Cause
200 A page of endpoints
401 authentication_required, invalid_api_key Credential missing or unusable
422 invalid_cursor The cursor does not continue from a known row
422 invalid_request limit is outside 1 to 100
503 storage_unavailable The database could not be reached

GET /endpoints/{endpoint_id}

One endpoint, in the same representation as one item of the listing.

Responses

Status Code Cause
200 The endpoint
401 authentication_required, invalid_api_key Credential missing or unusable
404 endpoint_not_found No endpoint with that ID exists
503 storage_unavailable The database could not be reached

PATCH /endpoints/{endpoint_id}

Changes an endpoint. Send only what should change; anything omitted is left exactly as it was.

Request body

Field Type Meaning
name string New label, 1 to 200 characters
is_active boolean Whether the endpoint accepts submissions
webhook_url string or null New destination, or null to remove the webhook

An id in the body is ignored. The endpoint ID is the primary key and cannot be changed.

Responses

Status Code Cause
200 The changed endpoint
401 authentication_required, invalid_api_key Credential missing or unusable
404 endpoint_not_found No endpoint with that ID exists
422 invalid_webhook_url The destination is malformed or not permitted
422 invalid_request The body failed schema validation
503 storage_unavailable The database could not be reached

Changing webhook_url to a different value generates a new signing secret and returns it in this response. See Changing the webhook.

Pagination

Both management list routes, GET /endpoints and GET /deliveries, page the same way.

Items come back newest first, ordered by creation time with the identifier as a tie-break, so a page is exactly reproducible. limit is 1 to 100 and defaults to 50; anything outside that is refused with 422 invalid_request rather than quietly clamped. A caller that asked for a thousand rows and silently received a hundred would page wrongly and never find out.

next_cursor is the identifier of the last item on the page. Pass it as cursor to continue:

curl "http://127.0.0.1:8000/deliveries?limit=50&cursor=whd_9a3f..." \
  -H "Authorization: Bearer $HYMICAL_KEY"

A full page always carries a cursor, even when it happens to be the last one, because knowing otherwise would cost an extra read on every page. A walk therefore ends on one empty page rather than on a null cursor.

A cursor that names no row is refused with 422 invalid_cursor rather than treated as the first page, so a caller that pages past a row somebody deleted learns about it instead of silently starting again from the top.

There is deliberately no total count. Counting an operational table on every page is not free, and nothing here needs the number.