CEEJAYDocs

Emit an event

Fan a custom event out to every webhook subscriber listening for it.

POST /v1/events/emit · requires events:emit

Body parameters

Prop

Type

Returns

Prop

Type

Each entry of results contains subscriptionId, name, status (delivered or failed) and httpStatus. httpStatus is the status the receiver responded with, null when nothing was sent because the URL is not allowed, and omitted when the request failed before any response, such as a timeout. A failure adds error.

Example

Request
curl -X POST https://ceejay.communitystudios.xyz/api/v1/events/emit \
  -H "Authorization: Bearer $CEEJAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "purchase.completed",
    "data": {
      "discordId": "409876543210987654",
      "sku": "season-pass",
      "value": 24.99
    },
    "idempotencyKey": "order-88213-event"
  }'

The TypeScript tab calls ceejay.events.emit from the TypeScript SDK. It returns the data of the response below.

200 OK
{
  "success": true,
  "data": {
    "event": "custom.purchase.completed",
    "subscribersNotified": 2,
    "results": [
      {
        "subscriptionId": 4,
        "name": "Ops relay",
        "status": "delivered",
        "httpStatus": 200
      },
      {
        "subscriptionId": 7,
        "name": "Data warehouse",
        "status": "failed",
        "httpStatus": 503,
        "error": "HTTP 503"
      }
    ],
    "duplicate": false
  }
}

Event names

A name is made of letters from A to Z in either case, digits, and the four characters ., _, : and -, such as purchase.completed or order:shipped_v2. It can be 1 to 93 characters long. You may write custom. in front of it yourself: those seven characters are not counted, and the event is delivered under the same name either way.

Spaces, accented letters, emoji and every other character are refused. The name is sent in the X-CEEJAY-Event header as well as in the body. A header cannot carry a line break, a control character or a character above U+00FF, and not every receiver reads the rest the same way, so a name is limited to characters that arrive unchanged everywhere.

Who receives it

Webhooks that are enabled and listen for custom.event (the dialog calls it An event sent through the API), for this exact name (custom.purchase.completed), or for * (Every event). The call returns 200 with subscribersNotified: 0 when none do.

Each webhook is called in turn, with a 5-second timeout, and the call returns once every one has been attempted, so its time grows with the number of webhooks. The delivery is signed and sent like any other event, as attempt 1. One call creates one delivery ID: every webhook receives the same body and the same X-CEEJAY-Delivery. A failed delivery counts toward the webhook's failure counter and is retried by CEEJAY, up to 4 more times. Webhooks has the signing, retry and disabling rules.

Your data is delivered as you sent it. CEEJAY adds nothing to it.

Retrying safely

Send an idempotencyKey with every call you might have to retry, such as after a timeout. It is a string of 1 to 128 characters that you choose, sent in the JSON body: an Idempotency-Key header is not read.

  • A repeat changes nothing. When the same request is sent again with its key, CEEJAY returns the response it returned the first time, with duplicate: true and the header Idempotent-Replayed: true.
  • One key is one request. Here the same request is the same event name and data. Two requests for one event need two keys, for example match-9:coins and match-9:xp. A key that was used for a different request, here or on any other request of the API, returns 409 with the code idempotency_key_reused, and nothing is changed.
  • A rejected request does not use up its key. A call that was rejected can be sent again with the same key once the cause is fixed.
  • A key belongs to the API key that sent it. Another API key of your community can send the same text, and it is a different key.
  • Keys are retained for 30 days. Use something you can rebuild after a crash, such as an order ID or a match ID.
  • On an instance that does not support idempotency keys yet, a call that sends one returns 503 with the code idempotency_unavailable, and nothing is changed. Community details reports whether keys are stored, under features.retryKeys. A body that sends idempotency_key gets no key here: this request reads idempotencyKey only.
  • Without a key, a repeated call is a new call.

A webhook cannot be recalled once sent, so CEEJAY never sends one key twice:

  • A call that returned 200 is stored against the key, also when some or all of its deliveries failed. CEEJAY retries those itself, so a repeat with the same key sends nothing. To send the event again, use a new key.
  • When a first request was started more than a minute ago and never finished, the same key returns 409 with the code idempotency_outcome_unknown.

Without a key, sending the same request twice sends the event twice.

Errors

StatuscodeerrorWhen
400bad_requestThe request body must be a JSON objectThe body is null, text, a number or a list. A request with no Content-Type header is read as text, so it gets this response too
400bad_requestevent is required and must be a stringevent is missing, empty or not text
400bad_requestevent name cannot exceed 100 charactersevent is longer than 100 characters
400bad_requestevent name must be 1-93 letters, digits, ".", "_", ":" or "-" (not counting a leading "custom.")The name contains a character that is not allowed, is custom. with nothing after it, or is longer than 93 characters after the prefix
400bad_requestdata cannot contain a null characterdata contains a null character (U+0000) somewhere, in a value or in a field name
400bad_requestdata is nested too deeplydata nests lists and objects more than 32 levels deep
400bad_requestdata must be valid Unicode textdata contains half of a surrogate pair somewhere, such as the JSON "\ud800" or an emoji cut in two
400bad_requestidempotencyKey must be a string of 1-128 charactersThe key is empty, longer than 128 characters or not text, or contains half of a surrogate pair, such as the JSON "\ud800" or an emoji cut in two
400bad_requestidempotencyKey cannot contain a null characterThe key contains a null character (U+0000)
404not_availableThis is not available on this instance yet.This instance has no webhook subscriptions yet. The body adds feature
409idempotency_key_reusedThis idempotencyKey was already used for a different requestYour API key already used this idempotencyKey for a different request: a request of another kind, or this one with other values. Nothing was changed
409idempotency_in_progressThe first request with this idempotencyKey is still running. Try again in a few seconds.The first request with this key is still being processed. Wait retryAfter seconds, then retry
409idempotency_outcome_unknownAn earlier request with this idempotencyKey may or may not have been applied. It was not sent again. Use a new key to send it again.A first request with this key was started more than a minute ago and never finished. CEEJAY cannot know whether it took effect, and does not repeat it. Check what happened, then use a new key if it has to be sent again
503idempotency_unavailableidempotencyKey cannot be used on this community yet, so nothing was changedYou sent a key, and this instance does not support idempotency keys yet. Nothing was changed. Community details reports it as features.retryKeys

A rejected event is not delivered or stored.

Every error body also carries requestId, and a 400 with the code bad_request names the invalid field in field. Match on code, not on the message text, which can change.

The errors every request shares (401, 403, 405, 429, 500) are on the Errors page.

On this page

Community StudiosPrivacyTerms