> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trylath.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Every Lath operation is POST https://platform.trylath.com/<operation name with dots replaced by slashes>, with a JSON body and `Authorization: Bearer <key>`. `email.send` is POST /email/send.
> Branch on `error.code`, never on `error.message`. Every refusal also carries `error.fix`, which names the next step.
> Send an `Idempotency-Key` header on any operation that is not retry-safe, so a retry cannot run it twice.
> A `lath_test_` key emails only the account's own members and sends no SMS; a `lath_live_` key reaches real recipients and is billed.
> The OpenAPI document, generated from the same registry as the routes, is at https://platform.trylath.com/openapi.json.

# Test mode

> Every project has a test environment beside the live one. It is for building against, and it is fenced so a loop left running cannot reach a stranger or cost anything.

## The key decides the environment

`account.signup` creates a live and a test environment for every project, each with its own secret and publishable key. Which one a request acts in is decided by the key it carries — there is no mode flag to set, and so none to forget.

A key says which it is. Secret keys start `lath_live_` or `lath_test_`; publishable keys start `lath_live_pk` or `lath_test_pk`. The two environments hold separate data, so the same person in live and in test is two records.

## Email reaches your own team, and nobody else

A test environment can send email only to the account's own members — people who have accepted an invitation. Any other address is refused with `test_mode_recipient` (403), and the refusal names `account.member.invite` as the way to add a colleague.

A member's address with a plus-tag counts as that member, so `dev+welcome@yourco.com` and `dev+bounce@yourco.com` both reach `dev@yourco.com`. One member can stand in for every case in a test suite.

Being invited is not enough. An address that was sent an invitation but has not accepted it is refused too, because nothing yet shows that anyone reads that mailbox.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://platform.trylath.com/email/send \
    -H "Authorization: Bearer $LATH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"to":"dev+welcome@yourco.com","subject":"Welcome flow","text":"Checking the copy."}'
  ```

  ```ts TypeScript theme={null}
  import { createLath } from "@trylath/sdk";

  const lath = createLath({ key: process.env.LATH_API_KEY });

  const { result } = await lath.email.send({
    to: "dev+welcome@yourco.com",
    subject: "Welcome flow",
    text: "Checking the copy.",
  });
  ```

  ```bash CLI theme={null}
  lath email send \
    --to dev+welcome@yourco.com \
    --subject 'Welcome flow' \
    --text 'Checking the copy.'
  ```
</CodeGroup>

## SMS is not sent from test

A test environment refuses every text with `test_mode_recipient`. An account's members are identified by email address, and nothing in the account proves control of a phone number — so rather than pretend, there is no test allow-list for SMS.

Send texts from the live environment, where the spending cap and the carrier registration both apply.

## Fifty messages a day

A test environment may create 50 messages in any 24 hours. The next is refused with `test_mode_limit` (429) until the window moves.

It is not a billing control — test sends are never billed — it is the difference between a loop that wastes a morning and one that costs a sending domain its reputation.

## What changes when you go live

Swap the test key for the live one. The operations, their inputs and their errors are the same on both, so nothing else in your code has to change.

Live mail goes to any address that consent and suppression allow, live texts send, and usage is billed and counted against the spending cap. Set that cap before the first live send.
