# Quick start

This sends your first test message through Flyanote's API with `curl`. A test send is free and never delivered: it shows your integration works. Then it adds your domain, which takes a live key. Steps 1 to 3 are sandbox steps; step 4 is live.

## Before you start

* An account. Today a person opens it in a browser, with an invite: accounts are by invite while Flyanote is in development, and sign-up opens early 2027\. To ask for one, email hello@flyanote.com.
* A test key. In the app, open API keys and make a key in Test mode that can “Send email” and “Check what happened to messages”. It's shown once, so copy it.
* `curl`, in a terminal.

Put the key in a variable, with your key in place of `fan_test_…`:

```
export FLYANOTE_API_KEY='fan_test_…'
```

## 1\. Check the key (sandbox)

```
curl -sS https://app.flyanote.com/api/v1/api-keys/current \
  -H "Authorization: Bearer $FLYANOTE_API_KEY"
```

The answer is the key itself, never its secret: its `mode` is `test`, and `scopes` lists `messages:send` and `messages:read`.

## 2\. Send a test message (sandbox, free)

Put the address you signed up with in place of `you@example.com`: until the account has a verified domain and credit, it sends only to its own members’ verified addresses.

```
curl -sS https://app.flyanote.com/api/v1/messages \
  -H "Authorization: Bearer $FLYANOTE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-1" \
  -d '{
    "from": {"address": "test@sandbox.flyanote.com", "name": "Quick start"},
    "to": "you@example.com",
    "subject": "My first test send",
    "text": "Sent through the Flyanote API."
  }'
```

* `test@sandbox.flyanote.com` is the sandbox sender: any test key may send from it before the account has a domain.
* `Idempotency-Key` is required: a value unique to this message. If the request fails or times out, send it again with the same key and the same body, and it won't send twice.
* To send another message, change the key, to `quickstart-2` say. The same key with a different body is refused with `409`, and the same key and body within 24 hours return the first answer without sending again.

The API answers `202 Accepted`: accepted isn't delivered, and a test message never is. `curl` prints the answer's body, like this one with the message's own `id`:

```
{"id": "msg_…", "status": "queued", "template": null, "warnings": []}
```

## 3\. See what happened (sandbox)

The key needs “Check what happened to messages”. Put the `id` from step 2 in place of `msg_…`:

```
export MESSAGE_ID='msg_…'
curl -sS "https://app.flyanote.com/api/v1/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $FLYANOTE_API_KEY"
```

Within a few seconds its `status` is `test`, where a test message ends. If it's `failed`, `reason.code` says why and `reason.retryable` whether sending it again may work: `tier0_recipient_not_member` means `to` wasn't a member's verified address.

## 4\. Add your domain (live)

Adding a domain sets it up for real sending, so it takes a live key: test keys can't. Make one that can “Add and remove domains” and “See your domains”, then put it in a variable:

```
export FLYANOTE_LIVE_KEY='fan_live_…'
```

Use your organization's own domain, not a free mailbox provider's, in place of `example.com`:

```
curl -sS https://app.flyanote.com/api/v1/domains \
  -H "Authorization: Bearer $FLYANOTE_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com"}'
```

The answer's `records` are the DNS records to add at your DNS host: a CNAME for DKIM and one for MAIL FROM, and a DMARC record it suggests when the domain has none. Each gives its `name` in full and its `host` as most DNS hosts want it. Adding the same domain again returns it, so a retry is safe.

Flyanote checks for the records on its own for 3 days. To check now, put the domain's `id` in place of `dom_…`:

```
export DOMAIN_ID='dom_…'
curl -sS -X POST "https://app.flyanote.com/api/v1/domains/$DOMAIN_ID/check" \
  -H "Authorization: Bearer $FLYANOTE_LIVE_KEY"
```

Then read the domain:

```
curl -sS "https://app.flyanote.com/api/v1/domains/$DOMAIN_ID" \
  -H "Authorization: Bearer $FLYANOTE_LIVE_KEY"
```

Once `verification.state` is `verified`, the domain can send. Until then, each record's `problem` says what's wrong with it in DNS: `missing`, `wrong_value`, `duplicate`, or, for DMARC, `invalid`.

Two states end the checking. `stopped` means 3 days passed without the records: checking again starts 3 more days. `setup_failed` means Flyanote couldn't set the domain up, and `setup.failure` says why (`domain_in_use`, say): checking again does nothing, so remove the domain and add it again once the cause is fixed.

## Sending live

A live send is the request in step 2 with your live key, from an address on your verified domain. It needs credit: while Flyanote is in development, we add credit on request. Live mail opens to customers with the private beta, and this page adds that step then.

## Next

* [API errors](https://flyanote.com/docs/errors): every code, and the steps that fix it.
* [The OpenAPI document](https://flyanote.com/openapi.json): every operation, copied from the running API each time this site updates.
* [Developers and AI agents](https://flyanote.com/developers): which steps still need a person.
