# Quickstart

Ten minutes from nothing to a delivered email. Every step is an HTTP call, so
this works the same from a terminal, a server, or an agent.

## 1. Get an API key

Keys come in two kinds: per project (`phk_…`) and per organization (`pha_…`).
An organization key can address any project it owns and is the only one that
can *create* projects, so it is the right credential to bootstrap with.

Create one in the dashboard under **Settings → API keys**. Pick the scopes it
needs; the **agent** preset selects everything except the two send scopes,
which is the right default for anything a model will hold. Set an expiry if the
key is for a fixed piece of work — anything from a day to ten years.

Issuing a key is an owner/admin action. A key with send scopes can mail your
entire list, so it carries the same authority as the sending credential itself.

The raw key is shown **once**, at creation. Pharos stores only its SHA-256 hash
and there is no way to recover it — put it straight into your secret store as
`PHAROS_API_KEY`.

Self-hosted deployments can also mint keys from the CLI:

```bash
pnpm org-key:create <org-slug> "Acme integration" --agent
pnpm api-key:create <project-slug> "Acme production" contacts:write,events:write,events:send
```

## 2. Create a project

Skip this if your project already exists.

```bash
curl -X POST https://pharosbase.com/api/v1/projects \
  -H "Authorization: Bearer $PHAROS_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme App", "sesFromEmail": "hi@acme.dev" }'
```

The slug is derived from the name and is globally unique — a collision returns
`409`. Project creation requires an organization key with `projects:write`; a
project key is pinned to a project that already exists, so it has nothing to
create.

## 3. Sync a contact

`POST /api/v1/contacts/sync` upserts contacts and their subscription state on
one list. It is idempotent: sending the same desired state twice changes
nothing.

```bash
curl -X POST https://pharosbase.com/api/v1/contacts/sync \
  -H "Authorization: Bearer $PHAROS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "acme-app",
    "listSlug": "newsletter",
    "source": "website_signup",
    "contacts": [{ "email": "reader@example.com", "subscribed": true }]
  }'
```

Requires `contacts:write`. Up to 5,000 contacts per call. Hard bounces and
complaints are protected — a sync cannot resubscribe them, and the response
reports how many it refused as `protectedSuppressions`.

Full contract: [Contact sync](/docs/contact-sync-api).

## 4. Preview a transactional email

`POST /api/v1/events` composes an email from your payload. **Preview does not
deliver anything** — it returns the composed subject, HTML and text so you (or
a reviewer) can look at it first.

```bash
curl -X POST https://pharosbase.com/api/v1/events \
  -H "Authorization: Bearer $PHAROS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "acme-app",
    "eventType": "welcome.completed",
    "idempotencyKey": "welcome:user-42",
    "notificationCategory": "product",
    "deliveryMode": "preview",
    "recipient": { "email": "reader@example.com", "firstName": "Sam" },
    "subject": "Welcome to Acme",
    "html": "<p>Glad you are here, Sam.</p>"
  }'
```

Requires `events:write` — nothing more. This is the call an agent can make.

## 5. Send it

Same request, `deliveryMode: "send"`.

```bash
  -d '{ …, "deliveryMode": "send" }'
```

This one requires `events:send`, a scope agent keys do not carry. The event is
persisted under its `(project, idempotencyKey)` pair and queued for delivery,
so repeating the request does not send twice.

Every delivery is recorded in the project's send log with its delivery and
open state — visible at `/<team>/<project>/sends` in the dashboard or through
`GET /api/sends`.

Full contract: [Transactional events](/docs/llm-and-transactional-events).

## 6. Point an agent at it

```bash
claude mcp add pharos -s user -t http https://pharosbase.com/api/mcp \
  -H "Authorization: Bearer $PHAROS_AGENT_KEY"
```

Nothing to install. The agent gets the same API you just used, bounded by the
same key — including the part where it cannot send. See
[MCP server](/docs/mcp-server).

## Next

- [Sending & deliverability](/docs/sending-providers) — connect your own Resend
  or SES account and turn on delivery tracking. Do this before a real campaign.
- [Unsubscribes & suppression](/docs/compliance) — what every list-bound
  template needs, and what protects your sending reputation.
- [Sending a campaign](/docs/campaigns) — the broadcast loop, end to end.
- [Core concepts](/docs/concepts) — what a list, template, campaign and send
  actually are.
- [Troubleshooting](/docs/troubleshooting) — when something does not behave.
- [API conventions](/docs/conventions) — errors, rate limits, identifiers.
- [Automations](/docs/pharos-automations) — turn that welcome email into a
  drip sequence.
