API Reference

Full reference for the Verne Relay HTTP API. All endpoints are served from https://api.vernesoft.com.

Examples are shown as raw curl requests and as calls through the official SDKs — see Installation to set one up. SDK snippets assume an initialized verne client.


1. Authentication

Every request must include a Bearer token in the Authorization header. A key is vrn_ followed by random characters; it does not name an API. The Relay API needs a key with the relay scope (shown as Events in the dashboard), chosen when the key is created. A key without it is refused with 403 Forbidden and the code forbidden (This API key is not authorized for Relay.). Keys created before scopes existed and named vrn_relay_... still work and open the Relay API only. There is no sandbox mode: every key delivers events for real. See API Keys.

Example header:

Authorization: Bearer vrn_9f8a7...

Keys are scoped to a single tenant, and you create and revoke them on the Dashboard → Keys page.


2. Send Event

POST /v1/relay/messages

Publishes a new event to all endpoints subscribed to the given event_type.

Request Body

FieldTypeRequiredDescription
event_typestringYesDot-notated event name (e.g. user.created).
payloadobjectYesArbitrary JSON payload delivered to subscribers.
idempotency_keystringNoSending the same key again within 12 hours returns the original message instead of creating a second one.
channelsstring[]NoRestrict delivery to endpoints listening on these channels.

Example Request

curl -X POST https://api.vernesoft.com/v1/relay/messages \
  -H 'Authorization: Bearer vrn_9f8a7...' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_type": "user.created",
    "payload": { "id": "123" },
    "idempotency_key": "evt_abc",
    "channels": ["team-a"]
  }'

Response

{
  "id": "msg_2hV9kLmNpQ",
  "event_type": "user.created",
  "status": "accepted",
  "timestamp": "2026-03-17T12:00:00Z"
}

Retries are safe. A second send with an idempotency_key that was already accepted does not create a second event and does not fail — it returns 202 with the originally accepted message, same id and same timestamp. A client that retries a request whose response it never saw ends up in the right state without having to tell a duplicate apart from a success. The window is 12 hours, counted from the first request that used the key; repeating the call does not extend it. This deduplicates the send, not the delivery: delivery is at-least-once, so the receiving endpoint should still deduplicate on svix-id.

Status CodeMeaning
202 AcceptedEvent queued for delivery — or already was, if you sent this idempotency_key before.
400 Bad RequestInvalid payload or missing required fields.
401 UnauthorizedMissing or invalid Bearer token.
402 Payment RequiredThe account is suspended for billing, or a renewal is unpaid. Retrying does not help — settle the balance.
429 Too Many RequestsRELAY_MESSAGE_LIMIT_REACHED: your plan's monthly Relay message limit is reached. Retry after the Retry-After header — the first of next month — or move to a higher plan.

Monthly message limit

Every plan includes a number of Relay messages a month, counted when a send is accepted: Free 10,000, Pro 1,000,000, Advanced 5,000,000 (a Contract plan has none). Reading or listing messages is not counted, and there is no monthly quota of API requests. At the limit a new send is refused until the next calendar month (UTC), with Retry-After set to the seconds until it starts:

HTTP/1.1 429 Too Many Requests
Retry-After: 1209600
{
  "error": {
    "code": "RELAY_MESSAGE_LIMIT_REACHED",
    "message": "You have sent the 10000 messages the Free plan includes this month. New messages are accepted again when the month ends (see Retry-After) or move to Pro for a higher limit.",
    "request_id": "req_…",
    "plan": "free",
    "limit": 10000,
    "current": 10000
  }
}

Moving to a higher plan lifts it at once. You are emailed at 80 % and at 100 % of the limit. The delivery log is kept for 7 days on Free, 90 on Pro and 180 on Advanced. The full list of limits and refusals is on Plans, Limits and Refusals.


3. List Events

GET /v1/relay/messages

Returns a paginated list of previously sent events for the current tenant.

Query Parameters

ParameterTypeDefaultDescription
limitinteger20Items per page. Values above 100 are clamped to 100 rather than rejected.
cursorstring—Pagination cursor from a previous response.
event_typestring—Filter by event type.

Example Request

curl 'https://api.vernesoft.com/v1/relay/messages?limit=5&event_type=user.created' \
  -H 'Authorization: Bearer vrn_9f8a7...'

Response

{
  "data": [
    {
      "id": "msg_2hV9kLmNpQ",
      "event_type": "user.created",
      "status": "accepted",
      "timestamp": "2026-03-17T12:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

status is always accepted — it records that Relay took the event, not what each subscriber endpoint did with it afterwards. Per-endpoint delivery state lives in the Console under Dashboard → Events.

next_cursor is null on the last page, so paginate until has_more is false rather than until the list comes back empty.


Error Format

All errors follow a consistent structure. The SDKs surface these fields as a typed error:

{
  "error": {
    "code": "invalid_payload",
    "message": "Field 'event_type' is required.",
    "request_id": "req_abc123"
  }
}

Include the request_id when contacting support for faster resolution.