---
name: rasket-setup
description: "Set up Rasket transactional and marketing email for this project: key, domain, DNS, webhook, template, contact, broadcast, automation."
allowed-tools: Bash(node:*) Bash(npx:*) Bash(curl:*) Read Write
---

<!-- Generated by `pnpm --filter @rasket/api recipe:generate` from `packages/api/src/mcp/recipe.ts`. Do not edit; regenerate. -->

# Set up Rasket

Rasket sends transactional and marketing email through one API at `https://api.rasket.com`. This skill sets it up for the project in front of you, in the order below, one step at a time: a key, a domain and its DNS, a first send, a webhook, and then the marketing half — a template, a contact, a segment, a broadcast and an automation. Stop at each step's **Done when** and confirm it before moving on.

## Rules

- **The key comes from `RASKET_API_KEY`, and only from there.** Ask the human to export it in the shell you run in. Never write the key into a file you create or commit — not source, not a config file, not a `.env` that is tracked, not a test fixture — and never echo it back.
- **Every request sends a `User-Agent`** naming the project, such as `User-Agent: acme-billing/1.0`. A request without one is refused with `403 validation_error`.
- **Authenticate with `Authorization: Bearer $RASKET_API_KEY`.** Paths below are relative to `https://api.rasket.com`.
- **Send with an `Idempotency-Key`** (1–256 characters) so a retried send cannot be sent twice.
- **The webhook signing secret is shown once.** Put it in the project's secret store, never in the repository.
- **A step marked _(not available yet)_** names something the API does not serve yet. Skip it and tell the human.
- Errors are `{ "statusCode", "name", "message" }`. Read `name`: it is the stable part.

## The recipe

1. **Create an API key** — Mint the `full_access` key the rest of the setup runs with. The first key of a team is created in the dashboard; a `full_access` key can mint more.
   - Call: `POST https://api.rasket.com/api-keys`
   - Needs: a `full_access` key
   - Done when: The response's `token` is exported as `RASKET_API_KEY` in the environment the agent runs in, and appears in no file.
   - Commonest failure: The token is shown once and cannot be read back. A lost token means creating another key, not retrieving the old one.
2. **Add a domain** — Add the domain mail is sent from. A subdomain such as `send.example.com` keeps its records apart from the apex's.
   - Call: `POST https://api.rasket.com/domains`
   - Needs: a `full_access` key
   - Done when: The response carries the domain's `id` and the `records` to publish.
   - Commonest failure: `409`: another team has already verified this name. List the team's domains first, so a second run does not try to add the same one again.
3. **Publish its DNS records** — Read the domain's records and publish every one of them at the DNS host.
   - Call: `GET https://api.rasket.com/domains/{domain_id}`
   - Needs: a `full_access` key
   - Done when: Every entry in `records` exists at the DNS host with its exact `type`, `name` and `value`, and its `priority` on an `MX` record.
   - Commonest failure: Most DNS hosts append the zone to the name, so pasting the full name publishes `send.example.com.example.com`. Enter only the part before the zone.
4. **Verify the domain** — Ask Rasket to look the records up now rather than at its next scheduled check.
   - Call: `POST https://api.rasket.com/domains/{domain_id}/verify`
   - Needs: a `full_access` key
   - Done when: Reading the domain again shows `status` `verified`; each record carries its own `status` while it is not.
   - Commonest failure: Asking before DNS has propagated. The call is limited to one a minute per domain (`429`) and answers `409 resource_locked` while a check is running: wait, then ask again.
5. **Set the sender identity** — Give the team the postal address every marketing email carries in its footer.
   - Call: `PATCH https://api.rasket.com/team`
   - Needs: a `full_access` key
   - Done when: The team answers with its postal address set.
   - Commonest failure: Skipping it. Transactional mail sends without one, but sending a broadcast is refused with `422 validation_error` until the team has a postal address.
6. **Send an email** — Send one message from an address on the verified domain, with an `Idempotency-Key` so a retry cannot send it twice.
   - Call: `POST https://api.rasket.com/emails`
   - Needs: a `sending_access` key
   - Done when: The response carries the email's `id`, and reading `/emails/{email_id}` shows what happened to each recipient.
   - Commonest failure: A `from` address on a domain that is not verified yet is refused with `403 validation_error`. Verify the domain first.
7. **Add a webhook and check its signature** — Register an HTTPS endpoint for the events the project handles. The response carries the signing secret, once.
   - Call: `POST https://api.rasket.com/webhooks`
   - Needs: a `full_access` key
   - Done when: The endpoint verifies a delivery's `svix-id`, `svix-timestamp` and `svix-signature` headers against the signing secret, which lives in the project's secret store.
   - Commonest failure: Verifying a parsed and re-serialised body. The signature covers the exact bytes received, so read the request body as text before verifying it.
8. **Create and publish a template** — Create a template with `POST /templates`, then publish it so sends can name it by id or alias.
   - Call: `POST https://api.rasket.com/templates/{id}/publish`
   - Needs: a `full_access` key
   - Done when: The template has a published version.
   - Commonest failure: Editing a published template and expecting sends to change. Sends resolve to the published version until you publish again.
9. **Add a contact** — Add a contact, recording only the consent the project actually holds.
   - Call: `POST https://api.rasket.com/contacts`
   - Needs: a `full_access` key
   - Done when: The response carries the contact's `id`. Creating the same address again returns that id, never a duplicate.
   - Commonest failure: A `properties` key that is not a declared contact property, or a value of the wrong type, is `422 validation_error`. Declare the property first.
10. **Create a segment** — Define the audience a broadcast goes to: by filter, by hand, or both.
    - Call: `POST https://api.rasket.com/segments`
    - Needs: a `full_access` key
    - Done when: Listing the segment's contacts returns the contacts you expect.
    - Commonest failure: A filter naming an undeclared property is `422 validation_error`, and a filter cannot be changed later: create a new segment instead.
11. **Send a broadcast** — Create a broadcast to the segment with `POST /broadcasts`, then send it or schedule it.
    - Call: `POST https://api.rasket.com/broadcasts/{id}/send`
    - Needs: a `full_access` key
    - Done when: The broadcast moves to `queued`, or to `scheduled` when `scheduled_at` was given.
    - Commonest failure: The compliance gate refuses before anything is queued: no verified sending domain, no postal address on the team, or a segment that does not resolve.
12. **Create an automation** — Create a workflow that runs per contact, starting from exactly one `trigger` step.
    - Call: `POST https://api.rasket.com/automations`
    - Needs: a `full_access` key
    - Done when: The automation exists with version 1 of its graph. It is `disabled` until you enable it, so nothing enrols by accident.
    - Commonest failure: A graph without exactly one `trigger`, with an unreachable step or a cycle, or naming a template, segment or event that does not exist, is `422 validation_error`.

## Scripts

Plain Node 20+, no dependencies, the key from `RASKET_API_KEY`. None of them writes a file.

- `node scripts/setup.mjs --domain send.example.com [--webhook-url https://example.com/webhooks/rasket] [--wait 600] [--dry-run]` — steps 2–4 and 7: adds the domain unless the team already has it, prints its DNS records, asks Rasket to verify it (polling for up to `--wait` seconds), and registers the webhook unless one already points at that URL. `--dry-run` makes only reads and prints the writes it would make.
- `node scripts/verify-dns.mjs --domain send.example.com [--wait 600]` — step 4 on its own: asks for a check and polls until the domain is verified or the wait runs out; exits 0 when verified and 3 when not yet.
- `node scripts/send-test.mjs --from hello@send.example.com --to you@example.com` — step 6: one test message, sent with an `Idempotency-Key`.

## References

- `references/api-quick-reference.md` — every operation, by tag.
- `references/dns-providers.md` — publishing `TXT`, `CNAME` and `MX` records at the common DNS hosts.
- `references/recipe.json` — this recipe as data.
- `rules/rasket.mdc` — the same recipe in the SDKs' vocabulary, for Cursor and Windsurf.
