# API errors

Every error from the Flyanote API is an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem (`application/problem+json`). Branch on its `code`, which never changes. Its `type` and `docs_url` link to the code's entry on this page.

* `title` is the same for every occurrence of a code.
* `detail` says what went wrong this time. Words in braces in a detail, like `{count}`, are filled in for each occurrence.
* `remediation` lists the steps that fix it, in order, as `{ "action": … }` objects: the same steps as below. A step that names an API operation, by its method and path, also has `operation_id`: the first one's `operationId` in the API's OpenAPI document. Follow the whole step: the operation may be only part of it.
* `request_id` identifies the request. Quote it when you contact support.

This page is generated from the API's error catalog, so it lists exactly the codes the API can return.

## Codes by status

* 400 Bad Request: [malformed\_request](https://flyanote.com/docs/errors#malformed-request), [duplicate\_cookie](https://flyanote.com/docs/errors#duplicate-cookie), [token\_invalid](https://flyanote.com/docs/errors#token-invalid), [oauth\_failed](https://flyanote.com/docs/errors#oauth-failed)
* 401 Unauthorized: [unauthenticated](https://flyanote.com/docs/errors#unauthenticated), [credentials\_invalid](https://flyanote.com/docs/errors#credentials-invalid), [api\_key\_missing](https://flyanote.com/docs/errors#api-key-missing), [api\_key\_invalid](https://flyanote.com/docs/errors#api-key-invalid), [api\_key\_revoked](https://flyanote.com/docs/errors#api-key-revoked), [api\_key\_expired](https://flyanote.com/docs/errors#api-key-expired), [two\_factor\_invalid](https://flyanote.com/docs/errors#two-factor-invalid), [two\_factor\_expired](https://flyanote.com/docs/errors#two-factor-expired), [passkey\_invalid](https://flyanote.com/docs/errors#passkey-invalid), [passkey\_user\_verification\_required](https://flyanote.com/docs/errors#passkey-user-verification-required), [reauth\_locked](https://flyanote.com/docs/errors#reauth-locked), [oauth\_wrong\_account](https://flyanote.com/docs/errors#oauth-wrong-account), [oauth\_reauth\_stale](https://flyanote.com/docs/errors#oauth-reauth-stale)
* 402 Payment Required: [insufficient\_credit](https://flyanote.com/docs/errors#insufficient-credit)
* 403 Forbidden: [csrf\_failed](https://flyanote.com/docs/errors#csrf-failed), [invite\_invalid](https://flyanote.com/docs/errors#invite-invalid), [password\_incorrect](https://flyanote.com/docs/errors#password-incorrect), [permission\_denied](https://flyanote.com/docs/errors#permission-denied), [api\_key\_scope\_missing](https://flyanote.com/docs/errors#api-key-scope-missing), [api\_key\_scope\_not\_allowed](https://flyanote.com/docs/errors#api-key-scope-not-allowed), [account\_closed](https://flyanote.com/docs/errors#account-closed), [account\_paused](https://flyanote.com/docs/errors#account-paused), [reauth\_required](https://flyanote.com/docs/errors#reauth-required), [reauth\_second\_factor\_required](https://flyanote.com/docs/errors#reauth-second-factor-required), [two\_factor\_blocked](https://flyanote.com/docs/errors#two-factor-blocked), [oauth\_no\_account](https://flyanote.com/docs/errors#oauth-no-account), [oauth\_email\_unverified](https://flyanote.com/docs/errors#oauth-email-unverified), [email\_not\_verified](https://flyanote.com/docs/errors#email-not-verified), [api\_key\_domain\_not\_allowed](https://flyanote.com/docs/errors#api-key-domain-not-allowed), [address\_app\_only](https://flyanote.com/docs/errors#address-app-only), [test\_key\_not\_allowed](https://flyanote.com/docs/errors#test-key-not-allowed)
* 404 Not Found: [not\_found](https://flyanote.com/docs/errors#not-found), [account\_not\_found](https://flyanote.com/docs/errors#account-not-found), [api\_key\_not\_found](https://flyanote.com/docs/errors#api-key-not-found), [passkey\_not\_found](https://flyanote.com/docs/errors#passkey-not-found), [session\_not\_found](https://flyanote.com/docs/errors#session-not-found), [oauth\_not\_connected](https://flyanote.com/docs/errors#oauth-not-connected), [oauth\_not\_configured](https://flyanote.com/docs/errors#oauth-not-configured), [domain\_not\_found](https://flyanote.com/docs/errors#domain-not-found), [sender\_profile\_not\_set](https://flyanote.com/docs/errors#sender-profile-not-set), [message\_not\_found](https://flyanote.com/docs/errors#message-not-found), [template\_not\_found](https://flyanote.com/docs/errors#template-not-found), [template\_version\_not\_found](https://flyanote.com/docs/errors#template-version-not-found), [brand\_kit\_version\_not\_found](https://flyanote.com/docs/errors#brand-kit-version-not-found), [contact\_not\_found](https://flyanote.com/docs/errors#contact-not-found), [field\_not\_found](https://flyanote.com/docs/errors#field-not-found), [tag\_not\_found](https://flyanote.com/docs/errors#tag-not-found), [topic\_not\_found](https://flyanote.com/docs/errors#topic-not-found), [form\_not\_found](https://flyanote.com/docs/errors#form-not-found), [form\_version\_not\_found](https://flyanote.com/docs/errors#form-version-not-found), [segment\_not\_found](https://flyanote.com/docs/errors#segment-not-found), [campaign\_email\_not\_found](https://flyanote.com/docs/errors#campaign-email-not-found), [campaign\_not\_found](https://flyanote.com/docs/errors#campaign-not-found), [saved\_section\_not\_found](https://flyanote.com/docs/errors#saved-section-not-found), [rendering\_not\_configured](https://flyanote.com/docs/errors#rendering-not-configured)
* 409 Conflict: [active\_account\_changed](https://flyanote.com/docs/errors#active-account-changed), [api\_key\_not\_revoked](https://flyanote.com/docs/errors#api-key-not-revoked), [api\_key\_limit\_reached](https://flyanote.com/docs/errors#api-key-limit-reached), [two\_factor\_already\_enabled](https://flyanote.com/docs/errors#two-factor-already-enabled), [two\_factor\_not\_enabled](https://flyanote.com/docs/errors#two-factor-not-enabled), [last\_sign\_in\_method](https://flyanote.com/docs/errors#last-sign-in-method), [oauth\_link\_required](https://flyanote.com/docs/errors#oauth-link-required), [oauth\_account\_in\_use](https://flyanote.com/docs/errors#oauth-account-in-use), [oauth\_connection\_exists](https://flyanote.com/docs/errors#oauth-connection-exists), [domain\_limit\_reached](https://flyanote.com/docs/errors#domain-limit-reached), [domain\_pending\_limit\_reached](https://flyanote.com/docs/errors#domain-pending-limit-reached), [address\_name\_taken](https://flyanote.com/docs/errors#address-name-taken), [address\_name\_lookalike](https://flyanote.com/docs/errors#address-name-lookalike), [address\_limit\_reached](https://flyanote.com/docs/errors#address-limit-reached), [address\_expectations\_outdated](https://flyanote.com/docs/errors#address-expectations-outdated), [idempotency\_key\_reused](https://flyanote.com/docs/errors#idempotency-key-reused), [already\_signed\_in](https://flyanote.com/docs/errors#already-signed-in), [template\_alias\_taken](https://flyanote.com/docs/errors#template-alias-taken), [template\_archived](https://flyanote.com/docs/errors#template-archived), [template\_not\_archived](https://flyanote.com/docs/errors#template-not-archived), [template\_not\_published](https://flyanote.com/docs/errors#template-not-published), [template\_limit\_reached](https://flyanote.com/docs/errors#template-limit-reached), [test\_key\_template\_limit\_reached](https://flyanote.com/docs/errors#test-key-template-limit-reached), [contact\_suppressed](https://flyanote.com/docs/errors#contact-suppressed), [field\_key\_taken](https://flyanote.com/docs/errors#field-key-taken), [tag\_name\_taken](https://flyanote.com/docs/errors#tag-name-taken), [topic\_name\_taken](https://flyanote.com/docs/errors#topic-name-taken), [topic\_in\_use](https://flyanote.com/docs/errors#topic-in-use), [topic\_archived](https://flyanote.com/docs/errors#topic-archived), [form\_in\_use](https://flyanote.com/docs/errors#form-in-use), [form\_limit\_reached](https://flyanote.com/docs/errors#form-limit-reached), [segment\_name\_taken](https://flyanote.com/docs/errors#segment-name-taken), [segment\_in\_use](https://flyanote.com/docs/errors#segment-in-use), [segment\_not\_archived](https://flyanote.com/docs/errors#segment-not-archived), [segment\_limit\_reached](https://flyanote.com/docs/errors#segment-limit-reached), [segment\_draft\_missing](https://flyanote.com/docs/errors#segment-draft-missing), [campaign\_archived](https://flyanote.com/docs/errors#campaign-archived), [campaign\_send\_open](https://flyanote.com/docs/errors#campaign-send-open), [campaign\_limit\_reached](https://flyanote.com/docs/errors#campaign-limit-reached), [campaign\_email\_limit\_reached](https://flyanote.com/docs/errors#campaign-email-limit-reached), [draft\_not\_moved](https://flyanote.com/docs/errors#draft-not-moved), [saved\_section\_limit\_reached](https://flyanote.com/docs/errors#saved-section-limit-reached)
* 412 Precondition Failed: [revision\_mismatch](https://flyanote.com/docs/errors#revision-mismatch)
* 413 Content Too Large: [payload\_too\_large](https://flyanote.com/docs/errors#payload-too-large)
* 415 Unsupported Media Type: [unsupported\_media\_type](https://flyanote.com/docs/errors#unsupported-media-type)
* 422 Unprocessable Content: [validation\_failed](https://flyanote.com/docs/errors#validation-failed), [email\_role\_mailbox](https://flyanote.com/docs/errors#email-role-mailbox), [domain\_invalid](https://flyanote.com/docs/errors#domain-invalid), [domain\_freemail](https://flyanote.com/docs/errors#domain-freemail), [domain\_reserved](https://flyanote.com/docs/errors#domain-reserved), [domain\_test\_only](https://flyanote.com/docs/errors#domain-test-only), [address\_name\_invalid](https://flyanote.com/docs/errors#address-name-invalid), [address\_name\_reserved](https://flyanote.com/docs/errors#address-name-reserved), [reply\_to\_invalid](https://flyanote.com/docs/errors#reply-to-invalid), [sender\_not\_ready](https://flyanote.com/docs/errors#sender-not-ready), [reply\_to\_unverified](https://flyanote.com/docs/errors#reply-to-unverified), [template\_category\_not\_allowed](https://flyanote.com/docs/errors#template-category-not-allowed), [template\_render\_failed](https://flyanote.com/docs/errors#template-render-failed), [message\_too\_large](https://flyanote.com/docs/errors#message-too-large), [document\_invalid](https://flyanote.com/docs/errors#document-invalid), [brand\_kit\_invalid](https://flyanote.com/docs/errors#brand-kit-invalid), [patch\_failed](https://flyanote.com/docs/errors#patch-failed), [template\_checks\_failed](https://flyanote.com/docs/errors#template-checks-failed), [export\_too\_large](https://flyanote.com/docs/errors#export-too-large), [consent\_record\_too\_large](https://flyanote.com/docs/errors#consent-record-too-large), [form\_publish\_blocked](https://flyanote.com/docs/errors#form-publish-blocked), [segment\_invalid](https://flyanote.com/docs/errors#segment-invalid), [segment\_too\_costly](https://flyanote.com/docs/errors#segment-too-costly), [section\_invalid](https://flyanote.com/docs/errors#section-invalid)
* 428 Precondition Required: [if\_match\_missing](https://flyanote.com/docs/errors#if-match-missing)
* 429 Too Many Requests: [two\_factor\_rate\_limited](https://flyanote.com/docs/errors#two-factor-rate-limited), [domain\_changes\_throttled](https://flyanote.com/docs/errors#domain-changes-throttled), [domain\_checks\_throttled](https://flyanote.com/docs/errors#domain-checks-throttled), [address\_claims\_throttled](https://flyanote.com/docs/errors#address-claims-throttled), [reply\_to\_rate\_limited](https://flyanote.com/docs/errors#reply-to-rate-limited), [rate\_limited](https://flyanote.com/docs/errors#rate-limited)
* 500 Internal Server Error: [internal](https://flyanote.com/docs/errors#internal)
* 503 Service Unavailable: [database\_unavailable](https://flyanote.com/docs/errors#database-unavailable), [auth\_unavailable](https://flyanote.com/docs/errors#auth-unavailable), [api\_keys\_unavailable](https://flyanote.com/docs/errors#api-keys-unavailable), [domains\_unavailable](https://flyanote.com/docs/errors#domains-unavailable), [pricing\_unavailable](https://flyanote.com/docs/errors#pricing-unavailable), [sending\_unavailable](https://flyanote.com/docs/errors#sending-unavailable), [segments\_unavailable](https://flyanote.com/docs/errors#segments-unavailable), [contacts\_unavailable](https://flyanote.com/docs/errors#contacts-unavailable), [rendering\_unavailable](https://flyanote.com/docs/errors#rendering-unavailable)

## 400 Bad Request

### `malformed_request`: Malformed request

* Status: 400 Bad Request
* Detail: The request body couldn't be parsed.
* Type: `https://flyanote.com/problems/malformed_request`

#### How to fix it

1. Send a body that is valid JSON, with `Content-Type: application/json`.

### `duplicate_cookie`: Duplicate cookie

* Status: 400 Bad Request
* Detail: The request carries more than one session or CSRF cookie, so it is ambiguous whose it is.
* Type: `https://flyanote.com/problems/duplicate_cookie`

#### How to fix it

1. Clear this site's cookies in the browser, then sign in again.
2. If it happens again right away, another site may be setting cookies for this one: contact Flyanote support with the `request_id`.

### `token_invalid`: Invalid or expired link

* Status: 400 Bad Request
* Detail: The link is invalid, has expired, or has already been used.
* Type: `https://flyanote.com/problems/token_invalid`

#### How to fix it

1. Ask for a new link: resend the verification email, or request another password reset.
2. Use the newest link you were sent. Each one works once, for a limited time.

### `oauth_failed`: Google sign-in didn't finish

* Status: 400 Bad Request
* Detail: Google's answer couldn't be used: it timed out, was already used, came back to a different browser or session, or didn't check out.
* Type: `https://flyanote.com/problems/oauth_failed`

#### How to fix it

1. Start again from the button in the same browser, and finish with Google within 10 minutes.
2. Or sign in another way: your password, a passkey, or a sign-in link.

## 401 Unauthorized

### `unauthenticated`: Not signed in

* Status: 401 Unauthorized
* Detail: This operation needs a signed-in session, and the request has none that is valid.
* Type: `https://flyanote.com/problems/unauthenticated`

#### How to fix it

1. Sign in (`POST /v1/app/auth/sign-in`), then retry.
2. For the public API, send an API key as `Authorization: Bearer fan_live_…` instead.

### `credentials_invalid`: Wrong email address or password

* Status: 401 Unauthorized
* Detail: The email address and password don't match an account.
* Type: `https://flyanote.com/problems/credentials_invalid`

#### How to fix it

1. Check the email address and password, then try again.
2. Forgot the password? Request a reset (`POST /v1/app/auth/password/forgot`).

### `api_key_missing`: API key missing

* Status: 401 Unauthorized
* Detail: This operation needs an API key, sent as `Authorization: Bearer fan_live_…`.
* Type: `https://flyanote.com/problems/api_key_missing`

#### How to fix it

1. Send the key in the `Authorization` header: the word `Bearer`, a space, then the whole key.
2. No key yet? An owner or admin of the account creates one in the Flyanote app, under API keys.

### `api_key_invalid`: Invalid API key

* Status: 401 Unauthorized
* Detail: The API key isn't valid: it's incomplete, mistyped, or not a key this environment issued.
* Type: `https://flyanote.com/problems/api_key_invalid`

#### How to fix it

1. Check that you sent the whole key: 59 characters, starting with `fan_live_` or `fan_test_`.
2. Check that the key is for this environment, then try again, or ask an owner or admin for a new key.

### `api_key_revoked`: API key revoked

* Status: 401 Unauthorized
* Detail: This API key has been revoked, so it no longer works.
* Type: `https://flyanote.com/problems/api_key_revoked`

#### How to fix it

1. Switch to another active key of the account, or ask an owner or admin to create a new one.
2. If nobody meant to revoke it, it may have been reported as leaked: tell the account's owners.

### `api_key_expired`: API key expired

* Status: 401 Unauthorized
* Detail: This API key has passed its expiry time, so it no longer works.
* Type: `https://flyanote.com/problems/api_key_expired`

#### How to fix it

1. Ask an owner or admin to create a new key, then switch to it. An expiry is fixed when a key is made.

### `two_factor_invalid`: That code didn't work

* Status: 401 Unauthorized
* Detail: The two-factor code or recovery code is wrong, or has already been used.
* Type: `https://flyanote.com/problems/two_factor_invalid`

#### How to fix it

1. Enter the newest 6-digit code from your authenticator app: codes change every 30 seconds.
2. Lost the app? Use one of your recovery codes instead. Each works once.

### `two_factor_expired`: Sign in again

* Status: 401 Unauthorized
* Detail: This sign-in's two-factor step has ended: it timed out, was already used, or had too many wrong codes.
* Type: `https://flyanote.com/problems/two_factor_expired`

#### How to fix it

1. Sign in again from the start (`POST /v1/app/auth/sign-in`, or a new sign-in link), then enter a fresh code.
2. Lost the app? Use a recovery code instead.

### `passkey_invalid`: That passkey didn't work

* Status: 401 Unauthorized
* Detail: The passkey couldn't be checked: it isn't one registered here, or the attempt timed out.
* Type: `https://flyanote.com/problems/passkey_invalid`

#### How to fix it

1. Try again: ask for fresh options first, then use the passkey within five minutes.
2. Sign in another way (password or a sign-in link), then add this passkey in Security settings.

### `passkey_user_verification_required`: This passkey can't be used here

* Status: 401 Unauthorized
* Detail: Flyanote needs your passkey to check it is you, with a PIN, face, or fingerprint, and it did not.
* Type: `https://flyanote.com/problems/passkey_user_verification_required`

#### How to fix it

1. Use a passkey on a device that asks for a PIN, face, or fingerprint, or set a PIN on your security key.
2. Or sign in with your password or a sign-in link.

### `reauth_locked`: Signed out after too many wrong passwords

* Status: 401 Unauthorized
* Detail: Too many wrong passwords were given for your account from signed-in sessions, so this one has been ended.
* Type: `https://flyanote.com/problems/reauth_locked`

#### How to fix it

1. Sign in again (`POST /v1/app/auth/sign-in`), then retry.
2. Forgot the password? Request a reset (`POST /v1/app/auth/password/forgot`). If you didn't try these passwords, reset it now: someone may have your session.

### `oauth_wrong_account`: That's a different Google account

* Status: 401 Unauthorized
* Detail: The Google account you chose isn't the one connected to your Flyanote user.
* Type: `https://flyanote.com/problems/oauth_wrong_account`

#### How to fix it

1. Try again and choose the Google account shown in Security settings.
2. Or confirm it's you another way: your password, a passkey, or an emailed link.

### `oauth_reauth_stale`: Google didn't ask you to sign in again

* Status: 401 Unauthorized
* Detail: Google didn't say you signed in to it recently, so it can't confirm it's you.
* Type: `https://flyanote.com/problems/oauth_reauth_stale`

#### How to fix it

1. Confirm it's you another way: ask for an emailed link (`POST /v1/app/auth/reauth/email`), your password, or a passkey.
2. Or sign out of Google, then try again: signing back in to Google counts.

## 402 Payment Required

### `insufficient_credit`: Insufficient credit

* Status: 402 Payment Required
* Detail: This needs {needed} {currency} of credit, more than the account has available.
* Type: `https://flyanote.com/problems/insufficient_credit`

#### How to fix it

1. Add credit to the account. During the beta, ask Flyanote support to add it; in-app top-ups aren't available yet.
2. To see what the account has, call `GET /v1/account/balance` with a key that has the `billing:read` scope, or, as an owner, an admin, or a billing member, open its Credit page in the Flyanote app.
3. To keep building meanwhile, send with a `fan_test_` key: test sends are free and never deliver.

## 403 Forbidden

### `csrf_failed`: Cross-site request refused

* Status: 403 Forbidden
* Detail: The request didn't come from the Flyanote app, or its CSRF token is missing or stale.
* Type: `https://flyanote.com/problems/csrf_failed`

#### How to fix it

1. Send app requests from the app itself: same origin, with the `x-csrf-token` header set from the `__Host-flyanote-csrf` cookie.
2. Fetch `GET /v1/app/session` to get a fresh token, then retry.

### `invite_invalid`: Invalid invite

* Status: 403 Forbidden
* Detail: The invite code isn't valid: it's unknown, already used, or expired.
* Type: `https://flyanote.com/problems/invite_invalid`

#### How to fix it

1. Check the code against the invite you were sent. Letters are not case-sensitive, and dashes are optional.
2. Each invite works once. Ask whoever invited you for a new one.

### `password_incorrect`: Wrong password

* Status: 403 Forbidden
* Detail: The current password is wrong.
* Type: `https://flyanote.com/problems/password_incorrect`

#### How to fix it

1. Enter your current password again.
2. If you don't remember it, sign out and request a password reset.

### `permission_denied`: Permission denied

* Status: 403 Forbidden
* Detail: Your role in this account doesn't allow this.
* Type: `https://flyanote.com/problems/permission_denied`

#### How to fix it

1. Ask an owner or admin of the account to do it, or to give you a role that can.
2. Check that the right account is active (`GET /v1/app/session`).

### `api_key_scope_missing`: Missing scope

* Status: 403 Forbidden
* Detail: This API key doesn't have the `{scope}` scope, which this operation needs.
* Type: `https://flyanote.com/problems/api_key_scope_missing`

#### How to fix it

1. Use a key that has the scope named in `detail`, or ask an owner or admin to create one. A key keeps the scopes it was made with.
2. Check which scopes this key has with `GET /v1/api-keys/current`.

### `api_key_scope_not_allowed`: Scope not allowed

* Status: 403 Forbidden
* Detail: A key can't do more than the person who makes it, and your role in this account doesn't allow what these scopes do: {scopes}.
* Type: `https://flyanote.com/problems/api_key_scope_not_allowed`

#### How to fix it

1. Make the key without the scopes listed in `detail`.
2. Or ask someone whose role allows them, such as an owner, to make the key.

### `account_closed`: Account closed

* Status: 403 Forbidden
* Detail: This account is closed, so its API keys no longer work, its sending domains can't be changed, and its forms can't go live.
* Type: `https://flyanote.com/problems/account_closed`

#### How to fix it

1. If the account shouldn't be closed, contact Flyanote support with the `request_id`.

### `account_paused`: Account paused

* Status: 403 Forbidden
* Detail: The account is paused, so it can't send, change its sending domains, or put a form live until it's resumed. Everything else still works.
* Type: `https://flyanote.com/problems/account_paused`

#### How to fix it

1. A pause follows a sending problem, usually too many bounces or complaints. Contact Flyanote support with the `request_id` to learn why, and what resumes it.
2. Mail accepted before the pause fails as `account_paused`, at no charge. Send it again once the account is resumed.

### `reauth_required`: Confirm it's you

* Status: 403 Forbidden
* Detail: This is a sensitive action, so it needs you to have signed in or confirmed it's you in the last 10 minutes.
* Type: `https://flyanote.com/problems/reauth_required`

#### How to fix it

1. Confirm it's you: `POST /v1/app/auth/reauth` with your password, or ask for a confirmation link (`POST /v1/app/auth/reauth/email`) and follow it in this browser.
2. Then retry the same request within 10 minutes.

### `reauth_second_factor_required`: Confirm it with your second factor

* Status: 403 Forbidden
* Detail: Two-factor is on, so changing how the account is protected needs a sign-in in the last 10 minutes, or a code from your app or a passkey.
* Type: `https://flyanote.com/problems/reauth_second_factor_required`

#### How to fix it

1. Confirm with a code from your authenticator app (`POST /v1/app/auth/reauth/two-factor`) or a passkey (`POST /v1/app/auth/reauth/passkey`).
2. Lost both? Sign out, sign in again with a recovery code, then retry within 10 minutes.

### `two_factor_blocked`: Two-factor sign-in is blocked

* Status: 403 Forbidden
* Detail: Too many wrong codes were entered for your account, so signing in with a code or a recovery code is blocked until the password is reset.
* Type: `https://flyanote.com/problems/two_factor_blocked`

#### How to fix it

1. Reset your password (`POST /v1/app/auth/password/forgot`): someone else may know it. Then sign in with a code.
2. A passkey still signs you in meanwhile.

### `oauth_no_account`: Google sign-in needs an invite

* Status: 403 Forbidden
* Detail: There is no Flyanote account for that Google account.
* Type: `https://flyanote.com/problems/oauth_no_account`

#### How to fix it

1. Sign up with your invite first: with Google (`POST /v1/app/auth/google/sign-up`) or your email address.
2. Already have an account with another address? Sign in with it, then connect Google in Security settings.

### `oauth_email_unverified`: Google can't vouch for that address

* Status: 403 Forbidden
* Detail: Google vouches only for Gmail addresses and Google Workspace accounts, so this Google account's address can't be used to sign up.
* Type: `https://flyanote.com/problems/oauth_email_unverified`

#### How to fix it

1. Already have a Flyanote account? Sign in with it, then connect this Google account in Security settings.
2. Otherwise sign up with your email address and invite: the link we mail proves the address. Then connect this Google account in Security settings, and it signs you in from then on.

### `email_not_verified`: Email address not verified

* Status: 403 Forbidden
* Detail: Verify your email address before doing this.
* Type: `https://flyanote.com/problems/email_not_verified`

#### How to fix it

1. Follow the link in the verification email.
2. No email? Resend it (`POST /v1/app/auth/verify-email/resend`), and check your spam folder.

### `api_key_domain_not_allowed`: Not one of this key's domains

* Status: 403 Forbidden
* Detail: This API key is restricted to other sending domains.
* Type: `https://flyanote.com/problems/api_key_domain_not_allowed`

#### How to fix it

1. Use a key that isn't restricted to other domains, or one made for this domain.
2. Or add the domain in the app, where an owner or admin can manage every domain.

### `address_app_only`: Only in the app

* Status: 403 Forbidden
* Detail: A Flyanote address can be released only in the app, since its name is used up for good.
* Type: `https://flyanote.com/problems/address_app_only`

#### How to fix it

1. An owner or admin can release it in the app, after signing in again.
2. An API key can still read the address, and send from it.

### `test_key_not_allowed`: Test keys can't do this

* Status: 403 Forbidden
* Detail: Test keys can't do this, because it changes live mail, reaches real services, or reads the account's contacts.
* Type: `https://flyanote.com/problems/test_key_not_allowed`

#### How to fix it

1. Use a live key (`fan_live_…`) with the scope the operation names.

## 404 Not Found

### `not_found`: Not found

* Status: 404 Not Found
* Detail: No operation matches this method and path.
* Type: `https://flyanote.com/problems/not_found`

#### How to fix it

1. Check the method and path against the API reference. Paths are case-sensitive, and all but `/health` start with `/v1`.

### `account_not_found`: Account not found

* Status: 404 Not Found
* Detail: The account doesn't exist, or you aren't a member of it.
* Type: `https://flyanote.com/problems/account_not_found`

#### How to fix it

1. List the accounts you're in (`GET /v1/app/session`) and use one of their ids.
2. Ask an owner of the account to invite you.

### `api_key_not_found`: API key not found

* Status: 404 Not Found
* Detail: No API key with this id exists in the active account.
* Type: `https://flyanote.com/problems/api_key_not_found`

#### How to fix it

1. List the account's keys (`GET /v1/app/api-keys`) and use one of their ids.
2. Check that the right account is active (`GET /v1/app/session`).

### `passkey_not_found`: Passkey not found

* Status: 404 Not Found
* Detail: You have no passkey with this id.
* Type: `https://flyanote.com/problems/passkey_not_found`

#### How to fix it

1. List your passkeys (`GET /v1/app/auth/passkeys`) and use one of their ids.

### `session_not_found`: Session not found

* Status: 404 Not Found
* Detail: You have no session with this id. It may have ended already.
* Type: `https://flyanote.com/problems/session_not_found`

#### How to fix it

1. List your sessions (`GET /v1/app/auth/sessions`) and use one of their ids.

### `oauth_not_connected`: Google isn't connected

* Status: 404 Not Found
* Detail: No Google account is connected to your Flyanote user.
* Type: `https://flyanote.com/problems/oauth_not_connected`

#### How to fix it

1. Connect one in Security settings (`POST /v1/app/auth/google/connect`).
2. To confirm it's you meanwhile, use your password, a passkey, or an emailed link.

### `oauth_not_configured`: Google sign-in isn't available here

* Status: 404 Not Found
* Detail: Google sign-in isn't set up in this environment.
* Type: `https://flyanote.com/problems/oauth_not_configured`

#### How to fix it

1. Sign in another way: your password, a passkey, or a sign-in link.
2. Check `GET /v1/app/auth/providers` before offering Google: it says whether Google is available here.

### `domain_not_found`: Domain not found

* Status: 404 Not Found
* Detail: No domain with this id exists in the account.
* Type: `https://flyanote.com/problems/domain_not_found`

#### How to fix it

1. List the account's domains (`GET /v1/domains`) and use one of their ids.
2. Check that the key or session is for the right account.

### `sender_profile_not_set`: Sender profile not set

* Status: 404 Not Found
* Detail: This account hasn't set its sender profile yet: the legal name, postal address, and contact that marketing email shows.
* Type: `https://flyanote.com/problems/sender_profile_not_set`

#### How to fix it

1. Set it with `PUT /v1/sender-profile` and a live key with `sender_profile:write`, or an owner or admin sets it in the app: `legal_name`, `postal_address` (`line1`, `city`, and `country` at least), and one `contact`.
2. Use the sender's real legal name and postal address: every marketing footer states them, as the law requires. If you don't know them, ask the account's owner.
3. Until it's set, marketing email can't be sent. Transactional email can.

### `message_not_found`: Message not found

* Status: 404 Not Found
* Detail: No message with this id exists in the account for this key.
* Type: `https://flyanote.com/problems/message_not_found`

#### How to fix it

1. Use the `id` from the send response, or list the messages (`GET /v1/messages`).
2. A test key sees only test messages, and a live key only live ones. A key restricted to sending domains sees only those domains' messages.

### `template_not_found`: Template not found

* Status: 404 Not Found
* Detail: No template with this id or alias exists in the account.
* Type: `https://flyanote.com/problems/template_not_found`

#### How to fix it

1. List the account's templates (`GET /v1/templates`) and use one of their ids or aliases. Aliases are lowercase.
2. If it worked before, its alias may have changed or it was deleted: find it in the list by its name.

### `template_version_not_found`: Template version not found

* Status: 404 Not Found
* Detail: The template has no version {version}.
* Type: `https://flyanote.com/problems/template_version_not_found`

#### How to fix it

1. List its versions (`GET /v1/templates/{template_id}/versions`) and use one of their numbers.
2. To use the newest version, leave `template_version` out.

### `brand_kit_version_not_found`: Brand kit version not found

* Status: 404 Not Found
* Detail: The brand kit has no version {version}.
* Type: `https://flyanote.com/problems/brand_kit_version_not_found`

#### How to fix it

1. List the kit's versions (`GET /v1/brand-kits/default/versions`) and use one of their numbers.

### `contact_not_found`: Contact not found

* Status: 404 Not Found
* Detail: No contact with this id exists in the account.
* Type: `https://flyanote.com/problems/contact_not_found`

#### How to fix it

1. Find the contact's id by searching the account's contacts (`POST /v1/contacts/search`) with their address or name in `query`.
2. If it worked before, the contact may have been deleted.

### `field_not_found`: Field not found

* Status: 404 Not Found
* Detail: No custom field with this id exists in the account.
* Type: `https://flyanote.com/problems/field_not_found`

#### How to fix it

1. List the account's custom fields (`GET /v1/contact-fields`) and use one of their ids.

### `tag_not_found`: Tag not found

* Status: 404 Not Found
* Detail: No tag with this id exists in the account.
* Type: `https://flyanote.com/problems/tag_not_found`

#### How to fix it

1. List the account's tags (`GET /v1/tags`) and use one of their ids.
2. To make a tag by its name, or find the one that has it, use `POST /v1/tags`.

### `topic_not_found`: Topic not found

* Status: 404 Not Found
* Detail: No topic with this id exists in the account.
* Type: `https://flyanote.com/problems/topic_not_found`

#### How to fix it

1. List the account's topics (`GET /v1/topics`) and use one of their ids.

### `form_not_found`: Form not found

* Status: 404 Not Found
* Detail: No form with this id exists in the account.
* Type: `https://flyanote.com/problems/form_not_found`

#### How to fix it

1. List the account's forms (`GET /v1/app/forms`) and use one of their ids.

### `form_version_not_found`: Form version not found

* Status: 404 Not Found
* Detail: The form has no published version with this number.
* Type: `https://flyanote.com/problems/form_version_not_found`

#### How to fix it

1. Read the form (`GET /v1/app/forms/{form_id}`): `live.version` is its newest version, and versions count up from 1.

### `segment_not_found`: Segment not found

* Status: 404 Not Found
* Detail: No segment with this id exists in the account.
* Type: `https://flyanote.com/problems/segment_not_found`

#### How to fix it

1. List the account's segments (`GET /v1/segments`) and use one of their ids.

### `campaign_email_not_found`: Campaign email not found

* Status: 404 Not Found
* Detail: No email with this id exists in that campaign in the account.
* Type: `https://flyanote.com/problems/campaign_email_not_found`

#### How to fix it

1. List the campaign's emails (`GET /v1/campaigns/{campaign_id}/emails`) and use one of their ids.

### `campaign_not_found`: Campaign not found

* Status: 404 Not Found
* Detail: No campaign with this id exists in the account.
* Type: `https://flyanote.com/problems/campaign_not_found`

#### How to fix it

1. List the account's campaigns (`GET /v1/campaigns`) and use one of their ids.

### `saved_section_not_found`: Saved section not found

* Status: 404 Not Found
* Detail: No saved section with this id exists in the account.
* Type: `https://flyanote.com/problems/saved_section_not_found`

#### How to fix it

1. List the account's saved sections (`GET /v1/app/sections`) and use one of their ids.

### `rendering_not_configured`: Rendering isn't available here

* Status: 404 Not Found
* Detail: Templates can't be checked or rendered in this environment yet.
* Type: `https://flyanote.com/problems/rendering_not_configured`

#### How to fix it

1. Save the template instead: each save answers with its findings, which need no render.
2. To send a message here, give it `subject` with `html`, `text`, or both, in place of `template`.
3. If you expected rendering here, contact Flyanote support with the `request_id`.

## 409 Conflict

### `active_account_changed`: Active account changed

* Status: 409 Conflict
* Detail: The request didn't name the session's active account in `x-flyanote-account`: it was made for another account, or named none. Nothing was changed.
* Type: `https://flyanote.com/problems/active_account_changed`

#### How to fix it

1. Read the session again (`GET /v1/app/session`) and use its `active_account_id` from then on: reload anything you show for the old account.
2. Send that id in `x-flyanote-account` on every request, and repeat the request only if it still makes sense in that account.

### `api_key_not_revoked`: API key not revoked

* Status: 409 Conflict
* Detail: This API key hasn't been revoked, so the templates it made weren't deleted.
* Type: `https://flyanote.com/problems/api_key_not_revoked`

#### How to fix it

1. Revoke the key in the app first (`POST /v1/app/api-keys/{id}/revoke`), then try again. A revoked key can make no more templates.
2. To delete one template a working key made, archive it, then delete it (`DELETE /v1/templates/{template_id}`).

### `api_key_limit_reached`: Too many API keys

* Status: 409 Conflict
* Detail: The account already has {limit} active API keys, the most it can have.
* Type: `https://flyanote.com/problems/api_key_limit_reached`

#### How to fix it

1. Revoke keys that are no longer in use (`POST /v1/app/api-keys/{id}/revoke`), then make the new one.
2. If the account really needs more active keys, contact Flyanote support.

### `two_factor_already_enabled`: Two-factor is already on

* Status: 409 Conflict
* Detail: Two-factor authentication is already on for your account.
* Type: `https://flyanote.com/problems/two_factor_already_enabled`

#### How to fix it

1. To move it to a new authenticator app, turn it off (`POST /v1/app/auth/two-factor/disable`), then set it up again.

### `two_factor_not_enabled`: Two-factor isn't on

* Status: 409 Conflict
* Detail: Two-factor authentication isn't on for your account, or its setup wasn't started or has been replaced.
* Type: `https://flyanote.com/problems/two_factor_not_enabled`

#### How to fix it

1. Start setting it up (`POST /v1/app/auth/two-factor/setup`), then confirm it with a code from your app.

### `last_sign_in_method`: Add another way to sign in first

* Status: 409 Conflict
* Detail: This is your only way to sign in, besides links by email, so it was not removed.
* Type: `https://flyanote.com/problems/last_sign_in_method`

#### How to fix it

1. Add a passkey, connect Google, or set a password with a reset link, then remove this one.

### `oauth_link_required`: Sign in another way first

* Status: 409 Conflict
* Detail: There is already a Flyanote account for that Google address, but that Google account is not connected to it.
* Type: `https://flyanote.com/problems/oauth_link_required`

#### How to fix it

1. Sign in with your password, a passkey, or a sign-in link.
2. Then connect Google in Security settings, and it signs you in from then on.

### `oauth_account_in_use`: That Google account is someone else's here

* Status: 409 Conflict
* Detail: That Google account is connected to another Flyanote user. Each one signs in to one user.
* Type: `https://flyanote.com/problems/oauth_account_in_use`

#### How to fix it

1. Choose a different Google account.
2. Or disconnect it from the other user first.

### `oauth_connection_exists`: Google is already connected

* Status: 409 Conflict
* Detail: A Google account is already connected to your Flyanote user.
* Type: `https://flyanote.com/problems/oauth_connection_exists`

#### How to fix it

1. To connect a different one, disconnect this one first (`DELETE /v1/app/auth/google`).

### `domain_limit_reached`: Too many domains

* Status: 409 Conflict
* Detail: The account already has {limit} domains of its own, the most it can have.
* Type: `https://flyanote.com/problems/domain_limit_reached`

#### How to fix it

1. Remove domains that are no longer in use (`DELETE /v1/domains/{id}`), then add the new one.
2. If the account really needs more domains, contact Flyanote support.

### `domain_pending_limit_reached`: Too many domains waiting to verify

* Status: 409 Conflict
* Detail: The account already has {limit} domains that haven't verified yet, the most it can have at once.
* Type: `https://flyanote.com/problems/domain_pending_limit_reached`

#### How to fix it

1. Finish setting up the domains already added: add their DNS records, then check again (`POST /v1/domains/{id}/check`).
2. Or remove a domain you won't use (`DELETE /v1/domains/{id}`), then add the new one.

### `address_name_taken`: That name is taken

* Status: 409 Conflict
* Detail: Another account holds this name, or held it. A released name is never given out again.
* Type: `https://flyanote.com/problems/address_name_taken`

#### How to fix it

1. Choose another name: add a word, your town, or your initials.
2. Check a name before claiming it: `POST /v1/app/addresses/availability`.

### `address_name_lookalike`: That name looks like one that is taken

* Status: 409 Conflict
* Detail: The name looks like one another account holds or held, at a glance (`0` for `o`, `rn` for `m`, a hyphen more or less), so recipients could mistake one for the other.
* Type: `https://flyanote.com/problems/address_name_lookalike`

#### How to fix it

1. Choose a name that reads differently, not only in characters that look alike.
2. Check a name before claiming it: `POST /v1/app/addresses/availability`.

### `address_limit_reached`: The account already has a Flyanote address

* Status: 409 Conflict
* Detail: An account can hold one Flyanote address.
* Type: `https://flyanote.com/problems/address_limit_reached`

#### How to fix it

1. Send from the address the account has: it is the domain with `mode` `flyanote_address` in `GET /v1/app/domains`.
2. To change its name, remove it (`DELETE /v1/app/domains/{id}`) and claim another. Its name is then used up for good.

### `address_expectations_outdated`: The expectations have changed

* Status: 409 Conflict
* Detail: The claim acknowledged an older text of the expectations for Flyanote addresses than the current one.
* Type: `https://flyanote.com/problems/address_expectations_outdated`

#### How to fix it

1. Read the current text (`GET /v1/app/addresses/expectations`), then claim again with its `version`.

### `idempotency_key_reused`: Idempotency key already used

* Status: 409 Conflict
* Detail: This `Idempotency-Key` was already used for a different request in the last 24 hours.
* Type: `https://flyanote.com/problems/idempotency_key_reused`

#### How to fix it

1. For a new message, send a new key: a UUID, or an id from your own system that is unique per message.
2. To retry the first request, send exactly the same body with the key: the retry returns its original response.

### `already_signed_in`: Already signed in

* Status: 409 Conflict
* Detail: This browser is already signed in, so the link was not used.
* Type: `https://flyanote.com/problems/already_signed_in`

#### How to fix it

1. Sign out (`POST /v1/app/auth/sign-out`), then open the link again.
2. If you meant to change your own password while signed in, use `POST /v1/app/auth/password/change`.

### `template_alias_taken`: Alias already in use

* Status: 409 Conflict
* Detail: Template {template\_id} already has this alias, and an alias names one template.
* Type: `https://flyanote.com/problems/template_alias_taken`

#### How to fix it

1. To change that template, update its draft (`PUT /v1/templates/{id}/draft`, with the id `detail` names) rather than creating another.
2. Or choose another alias. To free this one, give that template a new alias (`PUT /v1/templates/{id}/alias`), or archive and delete it.

### `template_archived`: Template archived

* Status: 409 Conflict
* Detail: The template is archived, so it can't be sent, edited, or published.
* Type: `https://flyanote.com/problems/template_archived`

#### How to fix it

1. Unarchive it (`POST /v1/templates/{template_id}/unarchive`), then retry.
2. Or use another template: list the active ones with `GET /v1/templates`.

### `template_not_archived`: Archive the template first

* Status: 409 Conflict
* Detail: Only an archived template can be deleted, so a template in use can't be deleted by mistake.
* Type: `https://flyanote.com/problems/template_not_archived`

#### How to fix it

1. Archive it (`POST /v1/templates/{template_id}/archive`): sends with it stop, and unarchiving undoes it.
2. Then delete it. Deleting is permanent, and frees its alias.

### `template_not_published`: Template not published

* Status: 409 Conflict
* Detail: The template has no published version to send yet.
* Type: `https://flyanote.com/problems/template_not_published`

#### How to fix it

1. Check its draft (`POST /v1/templates/{template_id}/validate`), then publish it (`POST /v1/templates/{template_id}/publish`).
2. Then send the same request again: nothing was stored, so the same `Idempotency-Key` works.

### `template_limit_reached`: Too many templates

* Status: 409 Conflict
* Detail: The account already has {limit} templates, the most it can have.
* Type: `https://flyanote.com/problems/template_limit_reached`

#### How to fix it

1. Delete templates you no longer use: archive each one, then delete it (`DELETE /v1/templates/{template_id}`).
2. If the account really needs more, contact Flyanote support.

### `test_key_template_limit_reached`: Too many templates from test keys

* Status: 409 Conflict
* Detail: Test keys have made {limit} templates in this account that nobody has published, the most they can. A leaked test key can't fill the account's limit.
* Type: `https://flyanote.com/problems/test_key_template_limit_reached`

#### How to fix it

1. Create the template with a live key (`fan_live_…`), or in the app.
2. Or clear out the templates test keys made: with a live key, archive each one, then delete it (`DELETE /v1/templates/{template_id}`). A revoked key's unpublished templates go in one call (`POST /v1/templates/delete-unpublished`).
3. A template that is published stops counting.

### `contact_suppressed`: Address can't be added

* Status: 409 Conflict
* Detail: This address is on the account's suppression list, so it can't be added. With `contacts:read`, or as a member, `suppression` says why, and since when where it can.
* Type: `https://flyanote.com/problems/contact_suppressed`

#### How to fix it

1. Only the person can join again, by signing up themselves through one of the account’s sign-up forms.
2. If they ask to be added, send them the link to a sign-up form instead.

### `field_key_taken`: Field key already in use

* Status: 409 Conflict
* Detail: Another custom field of the account has this key, and a key names one field.
* Type: `https://flyanote.com/problems/field_key_taken`

#### How to fix it

1. To use that field, find it in the account's custom fields (`GET /v1/contact-fields`) and set its value by its key.
2. Or choose another key. A field's key never changes once it's made, since templates read it.

### `tag_name_taken`: Tag name already in use

* Status: 409 Conflict
* Detail: Another tag of the account has this name, in some case.
* Type: `https://flyanote.com/problems/tag_name_taken`

#### How to fix it

1. List the account's tags (`GET /v1/tags`) to find the one with the name, or choose another name. Names are compared ignoring case.

### `topic_name_taken`: Topic name already in use

* Status: 409 Conflict
* Detail: Another topic of the account that isn’t archived has this name, in some case.
* Type: `https://flyanote.com/problems/topic_name_taken`

#### How to fix it

1. List the account's topics (`GET /v1/topics`) to find the one with the name, or choose another name. Names are compared ignoring case; an archived topic's name is free.

### `topic_in_use`: Topic in use

* Status: 409 Conflict
* Detail: Someone's consent, or a campaign email, names this topic, so it can't be deleted.
* Type: `https://flyanote.com/problems/topic_in_use`

#### How to fix it

1. Archive it instead (`POST /v1/topics/{topic_id}/archive`): it stops taking new subscribers and its history stays.

### `topic_archived`: Topic archived

* Status: 409 Conflict
* Detail: The topic is archived, so nobody can be subscribed to it.
* Type: `https://flyanote.com/problems/topic_archived`

#### How to fix it

1. Choose a topic that isn't archived: list them with `GET /v1/topics`, each with its `archived_at`.

### `form_in_use`: Form in use

* Status: 409 Conflict
* Detail: Someone's consent names a version of this form, so it can't be deleted.
* Type: `https://flyanote.com/problems/form_in_use`

#### How to fix it

1. Pause it instead (`POST /v1/app/forms/{form_id}/pause`): it stops taking sign-ups, and its history stays.

### `form_limit_reached`: Too many forms

* Status: 409 Conflict
* Detail: The account already has {limit} forms, the most it can have.
* Type: `https://flyanote.com/problems/form_limit_reached`

#### How to fix it

1. Delete forms you no longer use (`DELETE /v1/app/forms/{form_id}`). A form someone signed up through can't be deleted: it's the record of what they agreed to.
2. If the account really needs more, contact Flyanote support.

### `segment_name_taken`: Segment name already in use

* Status: 409 Conflict
* Detail: Another segment of the account that isn’t archived has this name, in some case.
* Type: `https://flyanote.com/problems/segment_name_taken`

#### How to fix it

1. List the account's segments (`GET /v1/segments`) to find the one with the name, or choose another name. Names are compared ignoring case; an archived segment's name is free.

### `segment_in_use`: Segment in use

* Status: 409 Conflict
* Detail: An email depends on this segment: a scheduled one names it, a send named it (one that went or was cancelled), or a campaign email names it.
* Type: `https://flyanote.com/problems/segment_in_use`

#### How to fix it

1. To archive it, read the segment (`GET /v1/segments/{segment_id}`): `used_by` counts the scheduled emails that name it. Change those emails' audiences first, or wait until they go.
2. A segment a send named (one that went or was cancelled), or one a campaign email names (its draft, or a version of it), can't be deleted: the email keeps it. Archive it instead (`PATCH /v1/segments/{segment_id}` with `archived: true`): it leaves the lists and frees its name, and its history stays.

### `segment_not_archived`: Archive the segment first

* Status: 409 Conflict
* Detail: Only an archived segment can be deleted, so a segment in use can't be deleted by mistake.
* Type: `https://flyanote.com/problems/segment_not_archived`

#### How to fix it

1. Archive it (`PATCH /v1/segments/{segment_id}` with `archived: true`): it leaves the lists and frees its name, and its history stays.
2. Then delete it. Deleting is permanent.

### `segment_limit_reached`: Too many segments

* Status: 409 Conflict
* Detail: The account already has {limit} segments, the most it can have, archived ones included.
* Type: `https://flyanote.com/problems/segment_limit_reached`

#### How to fix it

1. Delete segments you no longer use (`DELETE /v1/segments/{segment_id}`), each archived first (`PATCH /v1/segments/{segment_id}` with `archived: true`).
2. If the account really needs more, contact Flyanote support.

### `segment_draft_missing`: Segment has no draft

* Status: 409 Conflict
* Detail: The segment has no draft to discard: its applied rules are its only rules.
* Type: `https://flyanote.com/problems/segment_draft_missing`

#### How to fix it

1. Read the segment (`GET /v1/segments/{segment_id}`): `draft` is null while it has no draft.

### `campaign_archived`: Campaign archived

* Status: 409 Conflict
* Detail: The campaign is archived, so it and its emails can't be changed.
* Type: `https://flyanote.com/problems/campaign_archived`

#### How to fix it

1. Unarchive it (`POST /v1/campaigns/{campaign_id}/unarchive`), then retry.
2. Or copy it (`POST /v1/campaigns/{campaign_id}/copy`) and change the copy.

### `campaign_send_open`: Campaign still sending

* Status: 409 Conflict
* Detail: An email of the campaign is scheduled or sending, so the campaign can't be archived until that ends.
* Type: `https://flyanote.com/problems/campaign_send_open`

#### How to fix it

1. Wait for the send to finish, or cancel it, then archive the campaign.
2. Nothing was changed, so the same request works then.

### `campaign_limit_reached`: Too many campaigns

* Status: 409 Conflict
* Detail: The account already has {limit} campaigns, archived ones included, the most it can have.
* Type: `https://flyanote.com/problems/campaign_limit_reached`

#### How to fix it

1. If the account really needs more, contact Flyanote support.

### `campaign_email_limit_reached`: Too many campaign emails

* Status: 409 Conflict
* Detail: There’s no room for it: the most is {limit} emails, in the campaign or across the account’s campaigns.
* Type: `https://flyanote.com/problems/campaign_email_limit_reached`

#### How to fix it

1. Start a new campaign for the next emails (`POST /v1/campaigns`).
2. If the account really needs more, contact Flyanote support.

### `draft_not_moved`: The draft hasn't moved on

* Status: 409 Conflict
* Detail: Your work was built on revision {base}, and the draft is at revision {current}: only work built on an older revision is kept apart.
* Type: `https://flyanote.com/problems/draft_not_moved`

#### How to fix it

1. Save your work to the draft instead (`PUT` it with the draft’s `ETag` in `If-Match`).
2. Read the draft again (`GET` it) if you expected it to have moved on.

### `saved_section_limit_reached`: Too many saved sections

* Status: 409 Conflict
* Detail: The account already has {limit} saved sections, the most it can have.
* Type: `https://flyanote.com/problems/saved_section_limit_reached`

#### How to fix it

1. Delete saved sections you no longer use (`DELETE /v1/app/sections/{section_id}`).
2. If the account really needs more, contact Flyanote support.

## 412 Precondition Failed

### `revision_mismatch`: Changed since you read it

* Status: 412 Precondition Failed
* Detail: It has changed since {expected} (it's at {current} now), so nothing was changed.
* Type: `https://flyanote.com/problems/revision_mismatch`

#### How to fix it

1. Read it again (`GET` the draft or the brand kit) and make your change to what it returns.
2. Retry with its new `ETag` in `If-Match`. To replace it whatever it holds, `PUT` it with `If-Match: *`.

## 413 Content Too Large

### `payload_too_large`: Request body too large

* Status: 413 Content Too Large
* Detail: The request body is larger than this operation accepts.
* Type: `https://flyanote.com/problems/payload_too_large`

#### How to fix it

1. Send a smaller body. `POST /v1/messages` takes up to 2 MiB (each of `html` and `text` up to 256 KiB), and so do the template operations that take a document or data: creating a template, saving its draft, validating, and rendering. Every other operation takes up to 64 KiB.
2. For a large message, link images from a URL rather than inlining them as `data:` URIs.

## 415 Unsupported Media Type

### `unsupported_media_type`: Unsupported media type

* Status: 415 Unsupported Media Type
* Detail: This operation doesn't accept the request's `Content-Type`.
* Type: `https://flyanote.com/problems/unsupported_media_type`

#### How to fix it

1. Send the body as JSON, with `Content-Type: application/json`, or a JSON Patch with `Content-Type: application/json-patch+json` where the operation takes one.
2. Check the operation's request body in the API reference for the media types it accepts.

## 422 Unprocessable Content

### `validation_failed`: Validation failed

* Status: 422 Unprocessable Content
* Detail: The request has {count} invalid value(s). `errors` lists up to 100 of them.
* Type: `https://flyanote.com/problems/validation_failed`

#### How to fix it

1. Fix each entry in `errors`: `in` says which part of the request it is in, and `pointer` is a JSON Pointer to it.
2. Check the operation's schema in the API reference, then send the corrected request.

### `email_role_mailbox`: Flyanote doesn't email that address

* Status: 422 Unprocessable Content
* Detail: Addresses like `webmaster@`, `postmaster@`, and `abuse@` go to whoever runs a network or website, not to one person, so Flyanote never emails them. You'd miss every email about your account: links to sign in or verify, password resets, and security notices.
* Type: `https://flyanote.com/problems/email_role_mailbox`

#### How to fix it

1. Use an address of your own, like `ada@example.org`. A shared one your organization reads, like `office@` or `info@`, works too.

### `domain_invalid`: Not a usable domain

* Status: 422 Unprocessable Content
* Detail: The domain isn't a name that can be authenticated for sending.
* Type: `https://flyanote.com/problems/domain_invalid`

#### How to fix it

1. Send the domain alone, like `example.com` or `mail.example.com`: no `https://`, path, email address, or IP address.
2. Use a domain of at most 237 characters, with two to ten labels.

### `domain_freemail`: A freemail domain can't be used

* Status: 422 Unprocessable Content
* Detail: Free mailbox providers' domains (like gmail.com) can't be authenticated by anyone but the provider.
* Type: `https://flyanote.com/problems/domain_freemail`

#### How to fix it

1. Add a domain your organization owns and controls the DNS of.
2. Without one, send from a Flyanote address (`<name>.flyanote.email`) instead.

### `domain_reserved`: Flyanote's own domains can't be added

* Status: 422 Unprocessable Content
* Detail: The domain is, or is under, one of Flyanote's own domains.
* Type: `https://flyanote.com/problems/domain_reserved`

#### How to fix it

1. Add a domain your organization owns.
2. For an address on flyanote.email, use a Flyanote address instead of adding the domain.

### `domain_test_only`: Only test domains here

* Status: 422 Unprocessable Content
* Detail: Outside production, only test domains under `{zone}` can be added.
* Type: `https://flyanote.com/problems/domain_test_only`

#### How to fix it

1. Add a domain under the zone the detail names, like `yourname.` followed by that zone.
2. Add real domains in production.

### `address_name_invalid`: Not a usable address name

* Status: 422 Unprocessable Content
* Detail: A Flyanote address name is 3 to 40 lowercase letters and digits, which single hyphens may separate, like `yourclub`.
* Type: `https://flyanote.com/problems/address_name_invalid`

#### How to fix it

1. Drop any dots, spaces, accents, and underscores, and any hyphen at the start or end or next to another.
2. Check a name before claiming it: `POST /v1/app/addresses/availability`.

### `address_name_reserved`: That name is reserved

* Status: 422 Unprocessable Content
* Detail: The name, or one that looks like it, is reserved: Flyanote's own names, role words, and well-known brands.
* Type: `https://flyanote.com/problems/address_name_reserved`

#### How to fix it

1. Choose a name that's your organization's own, like its name or its initials.

### `reply_to_invalid`: Not a mailbox replies can go to

* Status: 422 Unprocessable Content
* Detail: The reply-to isn't an email address with a domain, or it's on one of Flyanote's own domains.
* Type: `https://flyanote.com/problems/reply_to_invalid`

#### How to fix it

1. Use the mailbox your organization reads, like `office@example.org`: one address, without a display name.

### `sender_not_ready`: Sender not ready

* Status: 422 Unprocessable Content
* Detail: The sender isn't on a domain or Flyanote address of this account that has finished setting up.
* Type: `https://flyanote.com/problems/sender_not_ready`

#### How to fix it

1. Send from an address on one of the account's domains whose `setup.status` is `ready` and whose `verification.dkim` and `verification.mail_from` are `verified` (`GET /v1/domains`).
2. A domain still verifying needs its DNS records in place: see its `records`. It verifies once they resolve.
3. To try the API before a sender is ready, send with a `fan_test_` key from `test@sandbox.flyanote.com`. Test sends never deliver.

### `reply_to_unverified`: Reply-to not confirmed

* Status: 422 Unprocessable Content
* Detail: The sender is a Flyanote address whose reply-to mailbox isn't confirmed yet, or the message's `reply_to` isn't that mailbox.
* Type: `https://flyanote.com/problems/reply_to_unverified`

#### How to fix it

1. Leave `reply_to` out: mail from a Flyanote address always replies to the address's confirmed mailbox.
2. If the address has none yet (its `reply_to.verified_at` is null in `GET /v1/domains`), an owner or admin confirms one in the app: Flyanote emails the mailbox a link, and its owner confirms there.

### `template_category_not_allowed`: Not a transactional template

* Status: 422 Unprocessable Content
* Detail: The template is a marketing email, and this operation sends transactional messages only.
* Type: `https://flyanote.com/problems/template_category_not_allowed`

#### How to fix it

1. Use a template whose `latest_version.category` is `transactional` (`GET /v1/templates/{template_id}`): receipts, password resets, and notifications.
2. Marketing email goes out as a campaign, to contacts who agreed to receive it.

### `template_render_failed`: Template couldn't be rendered

* Status: 422 Unprocessable Content
* Detail: The template couldn't be rendered with this data: `{reason}`.
* Type: `https://flyanote.com/problems/template_render_failed`

#### How to fix it

1. Render it with the same data (`POST /v1/templates/{template_id}/render`) to see where it stops.
2. For `loop_limit`, the loops ran past 1,000 items in all, so send fewer. For `time_limit` or `memory_limit`, the template does too much work for this data: simplify its loops. For `subject_empty`, the subject rendered blank: send the `data` it reads, or give it a fallback. Nothing was sent or charged.
3. For `template_invalid`, the published version no longer passes Flyanote's checks, and Flyanote has been told: check its draft (`POST /v1/templates/{template_id}/validate`) and publish it again.

### `message_too_large`: Message too large

* Status: 422 Unprocessable Content
* Detail: The rendered message is more than the {limit} KB a message can be.
* Type: `https://flyanote.com/problems/message_too_large`

#### How to fix it

1. Send less in `data`, such as fewer repeated rows, or trim the template. Render it with the same data (`POST /v1/templates/{template_id}/render`) to see its size.
2. Aim well under 102 KB: Gmail cuts off longer messages.

### `document_invalid`: Email document not valid

* Status: 422 Unprocessable Content
* Detail: The email document has {count} problem(s) that stop it being saved. `errors` lists up to 100 of them.
* Type: `https://flyanote.com/problems/document_invalid`

#### How to fix it

1. Fix each entry in `errors`: `pointer` is a JSON Pointer into the request body, and `code` and `detail` say what to change.
2. Check a document before saving with `POST /v1/templates/validate`, which lists every problem with its fix. The format is documented at https://flyanote.com/docs/email-format.

### `brand_kit_invalid`: Brand kit not valid

* Status: 422 Unprocessable Content
* Detail: The brand kit has {count} problem(s) that stop it being saved. `errors` lists up to 100 of them.
* Type: `https://flyanote.com/problems/brand_kit_invalid`

#### How to fix it

1. Fix each entry in `errors`: `pointer` is a JSON Pointer into the request body.
2. Check a kit before saving with `POST /v1/brand-kits/default/validate`.

### `patch_failed`: Patch couldn't be applied

* Status: 422 Unprocessable Content
* Detail: Operation {index} of the JSON Patch (counting from 0) couldn't be applied, so nothing was changed. `errors` says why.
* Type: `https://flyanote.com/problems/patch_failed`

#### How to fix it

1. Read it again (a `GET` of the same path) and build the patch on what it returns: each `path` must exist, and array indexes shift as items are added or removed.
2. A failed `test` operation means the value changed since you read it.

### `template_checks_failed`: Draft not ready to publish

* Status: 422 Unprocessable Content
* Detail: The draft has {count} error(s) that stop it being published. `findings` lists up to 100 of them.
* Type: `https://flyanote.com/problems/template_checks_failed`

#### How to fix it

1. Fix each finding whose `severity` is `reject`, `block`, or `bug`: `pointer` points into the draft, and a `patch` fix is a JSON Patch to apply with `PATCH /v1/templates/{template_id}/draft`.
2. Then publish again, with the draft's new revision in `If-Match`.

### `export_too_large`: Export too large

* Status: 422 Unprocessable Content
* Detail: This export is larger than one export can be: at most {limit} contacts, and fewer when each holds a lot of fields.
* Type: `https://flyanote.com/problems/export_too_large`

#### How to fix it

1. Export part of them at a time: one list, one tag, or one status, or narrow it with search text. Together, the parts hold everyone.
2. If one list or tag alone is too large, contact Flyanote support with the `request_id`.

### `consent_record_too_large`: Consent record too large

* Status: 422 Unprocessable Content
* Detail: This contact's consent record is larger than one download can be.
* Type: `https://flyanote.com/problems/consent_record_too_large`

#### How to fix it

1. Contact Flyanote support with the `request_id`: a record this large is unusual, and we'll look into it.

### `form_publish_blocked`: Form not ready to publish

* Status: 422 Unprocessable Content
* Detail: The form can't be published yet: `reasons` lists {count} thing(s) to fix.
* Type: `https://flyanote.com/problems/form_publish_blocked`

#### How to fix it

1. For `sender_not_ready`, set up a sending domain or a Flyanote address and let it verify: the confirmation email needs a sender. For `sender_profile_missing`, fill in the sender profile: the form says who is asking.
2. For each reason with a `pointer`, fix that part of the draft and save it (`PUT /v1/app/forms/{form_id}/draft`). A list or a custom field that isn't found or is archived needs another, or taking out.
3. Then publish again, with the draft's new revision in `If-Match`.

### `segment_invalid`: Segment rules invalid

* Status: 422 Unprocessable Content
* Detail: The rules can’t be stored or run as written: `errors` points at the first rule refused and says why.
* Type: `https://flyanote.com/problems/segment_invalid`

#### How to fix it

1. Fix the rule at `errors[0].pointer`. `errors[0].code` says what’s wrong: a key or value the rule tree doesn’t take, a list, tag, or field the account doesn’t have (deleted since?), an operator the field’s type doesn’t take, or a bound passed (30 rules in a tree, 15 in a group, 3 levels, `in_last` at most 3650 days).
2. A `clicked`, `not_clicked`, or `opened` rule is refused as `engagement_unavailable` where engagement isn’t recorded yet. Leave it out until it is.

### `segment_too_costly`: Segment too costly to count

* Status: 422 Unprocessable Content
* Detail: Counting these rules over the audience needed more than one count may: longer than 5 s for all of its work, or reading more people whose consent to one of its lists has passed its expiry date than one count can.
* Type: `https://flyanote.com/problems/segment_too_costly`

#### How to fix it

1. Simplify the rules: fewer `any` groups and negatives, and no rule that matches almost everyone. Then count again.
2. If a segment you need can’t be counted, or its lists hold many people whose consent has passed its expiry date, contact Flyanote support with the `request_id`.

### `section_invalid`: Section not valid

* Status: 422 Unprocessable Content
* Detail: The section has {count} problem(s) that stop it being saved. `errors` lists up to 100 of them.
* Type: `https://flyanote.com/problems/section_invalid`

#### How to fix it

1. Fix each entry in `errors`: `pointer` is a JSON Pointer into the request body, and `code` and `detail` say what to change. The format is documented at https://flyanote.com/docs/email-format.

## 428 Precondition Required

### `if_match_missing`: If-Match header needed

* Status: 428 Precondition Required
* Detail: This change needs an `If-Match` header, so it can't overwrite someone else's edit.
* Type: `https://flyanote.com/problems/if_match_missing`

#### How to fix it

1. Read it first, then send its `ETag` as `If-Match`: the change applies only if nobody changed it since.
2. On a `PUT`, `If-Match: *` replaces it whatever it holds.

## 429 Too Many Requests

### `two_factor_rate_limited`: Two-factor sign-in is paused for an hour

* Status: 429 Too Many Requests
* Detail: Too many wrong codes were entered for your account in a row, so two-factor sign-in is paused for an hour.
* Type: `https://flyanote.com/problems/two_factor_rate_limited`

#### How to fix it

1. Wait an hour, then sign in again. Recovery codes are refused while it is paused too.
2. If you didn't enter these codes, someone has your password: reset it once the hour is up.

### `domain_changes_throttled`: Too many domain changes

* Status: 429 Too Many Requests
* Detail: The account has added or removed {limit} domains in the last hour, the most it can.
* Type: `https://flyanote.com/problems/domain_changes_throttled`

#### How to fix it

1. Wait up to an hour, then retry.
2. Adding a domain the account already has isn't a change: it returns the domain.

### `domain_checks_throttled`: Too many domain checks

* Status: 429 Too Many Requests
* Detail: The account has asked for {limit} domain checks in the last hour, the most it can.
* Type: `https://flyanote.com/problems/domain_checks_throttled`

#### How to fix it

1. Wait up to an hour, then ask again. Flyanote keeps checking each domain on its own schedule meanwhile.
2. A domain's `verification.next_check_at` says when it's next checked anyway.

### `address_claims_throttled`: Too many addresses claimed

* Status: 429 Too Many Requests
* Detail: The account has claimed {limit} Flyanote addresses in the last 30 days, the most it can.
* Type: `https://flyanote.com/problems/address_claims_throttled`

#### How to fix it

1. Keep the address the account has, or wait until 30 days after its oldest claim, then retry.
2. If the account really needs another, contact Flyanote support.

### `reply_to_rate_limited`: Too many reply-to emails

* Status: 429 Too Many Requests
* Detail: The account has asked for as many reply-to confirmation emails as it can for now: {hour} an hour, {day} a day, and {mailboxes} different mailboxes a day.
* Type: `https://flyanote.com/problems/reply_to_rate_limited`

#### How to fix it

1. Check the inbox, and its spam folder, for the emails already sent: the newest link works for 3 days.
2. Or wait, then ask again: up to an hour, or up to a day after asking about several mailboxes.

### `rate_limited`: Too many requests

* Status: 429 Too Many Requests
* Detail: The `{policy}` rate limit is used up for now. Retry in {retry\_after} second(s).
* Type: `https://flyanote.com/problems/rate_limited`

#### How to fix it

1. Wait for the number of seconds in the `Retry-After` header (also `retry_after`), then retry.
2. Where the response has a `RateLimit` header, pace requests with it: `r` is how many are left now, and `RateLimit-Policy` states each limit as `q` requests per `w` seconds.

## 500 Internal Server Error

### `internal`: Internal error

* Status: 500 Internal Server Error
* Detail: Something went wrong on our side, and it has been reported.
* Type: `https://flyanote.com/problems/internal`

#### How to fix it

1. Retry with exponential backoff. For a request that creates or sends something, reuse its `Idempotency-Key` so the retry can't act twice.
2. If it keeps failing, contact Flyanote support with the `request_id`.

## 503 Service Unavailable

### `database_unavailable`: Database unavailable

* Status: 503 Service Unavailable
* Detail: The database is temporarily unavailable.
* Type: `https://flyanote.com/problems/database_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `auth_unavailable`: Sign-in unavailable

* Status: 503 Service Unavailable
* Detail: Signing in isn't available right now.
* Type: `https://flyanote.com/problems/auth_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `api_keys_unavailable`: API keys unavailable

* Status: 503 Service Unavailable
* Detail: API keys can't be checked or created right now.
* Type: `https://flyanote.com/problems/api_keys_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `domains_unavailable`: Domains unavailable

* Status: 503 Service Unavailable
* Detail: Domains can't be read or changed right now.
* Type: `https://flyanote.com/problems/domains_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff. Adding a domain again is safe: it returns the one already added.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `pricing_unavailable`: No price in effect

* Status: 503 Service Unavailable
* Detail: Live sends can't be priced right now, so none is accepted: Flyanote never sends without charging.
* Type: `https://flyanote.com/problems/pricing_unavailable`

#### How to fix it

1. Retry later with the same `Idempotency-Key`. This lasts until a price takes effect, which can take hours or days, so back off to a retry every few minutes.
2. To keep building meanwhile, send with a `fan_test_` key: test sends are free and never deliver.
3. If it lasts more than a day, contact Flyanote support with the `request_id`.

### `sending_unavailable`: Sending unavailable

* Status: 503 Service Unavailable
* Detail: Messages can't be accepted right now.
* Type: `https://flyanote.com/problems/sending_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff and the same `Idempotency-Key`: a retry never sends twice.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `segments_unavailable`: Segments unavailable

* Status: 503 Service Unavailable
* Detail: Segments can’t be counted or changed right now: another request of the account is using them, or this request has no time left for a count.
* Type: `https://flyanote.com/problems/segments_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff: nothing was changed.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `contacts_unavailable`: Contacts unavailable

* Status: 503 Service Unavailable
* Detail: Contacts can't be changed right now.
* Type: `https://flyanote.com/problems/contacts_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff: nothing was changed.
2. Reading contacts still works. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.

### `rendering_unavailable`: Rendering unavailable

* Status: 503 Service Unavailable
* Detail: Templates can't be rendered right now.
* Type: `https://flyanote.com/problems/rendering_unavailable`

#### How to fix it

1. Retry after a few seconds, with exponential backoff. For a send, reuse its `Idempotency-Key`: a retry never sends twice.
2. If it lasts more than a few minutes, contact Flyanote support with the `request_id`.
