# 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`.

> [!PROMPT]
> Add Kirora to my app: identify a contact when they sign up, track Order Completed with
> an Idempotency-Key from the order id, and send the receipt with a stored template.

## 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:

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

## 2. Check it

With cURL:

```sh
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](/docs/api/authentication).

## 3. Add a contact

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

```sh
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](/docs/api/transactional#test-addresses): 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:

```sh
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](/docs/api/idempotency) and the standard names in [E-commerce events](/docs/api/ecommerce-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:

```sh
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](/docs/api/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](/docs/api/transactional).

You should see the email in your inbox.

## Next steps

- [Transactional messages](/docs/api/transactional) — stored templates, push, and sending to many at once
- [Webhooks](/docs/api/webhooks) — learn what happened to each message
- [Errors](/docs/api/errors) — every code and which to retry
- [Pagination](/docs/api/pagination) — walk any list with a cursor
- [Agents](/docs/api/agents) — let an AI assistant work in your workspace

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`.