Menu

Quickstart

A key, a contact, an event and a first email, in five requests

Five requests: check a key, add a contact, record what they did, and send them an email. The API is at https://api.kirora.io.

1 Create a key

In the dashboard, open Developer → API keys and create a key with these scopes: contacts:write, events:write and transactional:send. Copy it now: it is shown once. Then set it in your shell:

export KIRORA_API_KEY=kirora_...
export KIRORA_API_URL=https://api.kirora.io

2 Check it

With cURL:

curl "$KIRORA_API_URL/v1/me" -H "Authorization: Bearer $KIRORA_API_KEY"

The answer names your workspace and the key’s scopes. A 401 means the key is wrong or revoked; see Authentication.

3 Add a contact

With cURL, identify creates the contact, or updates the one with that externalId or email:

curl "$KIRORA_API_URL/v1/contacts/identify" \
  -H "Authorization: Bearer $KIRORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"externalId":"user_123","email":"ada@example.com","firstName":"Ada","emailConsent":true}'

example.com is a test address: sends to it go through every check and are never delivered. Use a real address when you want to see the email arrive.

4 Track an event

Events drive journeys, segments and conversion goals. Name them for what the person did. With cURL:

curl "$KIRORA_API_URL/v1/events/track" \
  -H "Authorization: Bearer $KIRORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001-completed" \
  -d '{"externalId":"user_123","eventName":"Order Completed","properties":{"total":42.5,"currency":"USD"}}'

occurredAt is optional and defaults to now. Send it to record when the event really happened: an ISO 8601 timestamp with Z or an offset (2026-10-01T09:30:00Z, 2026-10-01T10:30:00+01:00), at most 24 hours ahead.

The Idempotency-Key makes a retry safe: the same key never records the event twice. See Idempotency and the standard names in E-commerce events.

Naming events

  • One fixed name per action, in Title Case, object then past-tense verb: Order Completed, Plan Upgraded. Anything that varies, such as an id, a price or a plan, goes in properties, never in the name.
  • Names are case-sensitive. order completed will not start a journey waiting for Order Completed.
  • Up to 128 characters, no control characters (newlines, tabs). Names beginning with contact. are reserved for the events Kirora records itself.
  • At most 1,000 distinct names per workspace. A new name past that is refused with EVENT_NAME_LIMIT_REACHED; GET /v1/events/names reports the count in meta.nameUsage. DELETE /v1/events/names/{name} removes a name sent by mistake, with all its events, unless a journey, segment, campaign or goal still reads it.

5 Send an email

Email goes out from a domain you have verified: add one under Settings → Email → Domains and publish the records it shows. Then send with cURL:

curl "$KIRORA_API_URL/v1/transactional/messages" \
  -H "Authorization: Bearer $KIRORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001-receipt" \
  -d '{
    "to": { "externalId": "user_123" },
    "email": {
      "subject": "Thanks for your order, {{ contact.firstName | default: \"there\" }}",
      "from": "Shop <orders@mail.yourdomain.com>",
      "html": "<p>Your order is confirmed. We will tell you when it ships.</p>"
    }
  }'

The answer lists the message it queued; its outcome arrives on webhooks or from GET /v1/transactional/messages/{id}. From an address on a domain that is not verified, the request fails with 403 SENDING_DOMAIN_UNVERIFIED. For stored templates, push, and sending to many at once, read Transactional messages.

You should see the email in your inbox.

Next steps

Marketing email (campaigns and journeys) needs the contact’s consent and your postal address; set both up in the dashboard, then build in it or over /v1.