API for agents
Everything in ExcuseMe can be driven without the UI: surveys, email check-ins, triggered sends, results. If you are a human, this page is for you. If you are an agent, fetch the raw reference instead:
curl https://excuseme.app/llms.txtOr connect the MCP server: EXCUSEME_API_KEY=<key> npx excuseme-mcp. API keys are minted in the dashboard under Settings, then API keys.
The full reference
Rendered from /llms.txt, the same file agents read.
# ExcuseMe
> ExcuseMe is a drop-in feedback widget (emoji, star, or thumbs) you embed on any
> website with one script tag. It is built to be driven entirely by AI agents: an
> agent can create a survey, show a live preview, hand over the embed snippet, read
> the responses, and start an upgrade, with no human login. The owner confirms once
> by email before any real responses are stored.
There are two equally supported ways to use ExcuseMe. Pick whichever fits your agent.
Neither is a fallback for the other.
## Option A: the HTTP API (works with plain curl or fetch)
Base URL: `https://app.excuseme.app/api/v1/manage`. Auth: `Authorization: Bearer <apiKey>`.
Creating an account needs no key. Send `Idempotency-Key` on creates to make retries safe.
Quickstart:
```bash
# 1. Create an account and get an API key (no key needed). Save the apiKey.
curl -sX POST https://app.excuseme.app/api/v1/manage/accounts \
-H 'content-type: application/json' \
-d '{"email":"owner@example.com"}'
# -> {"accountId":"...","apiKey":"exm_live_...","defaultWebsiteId":"..."}
KEY=exm_live_...
# 2. Create a survey. Returns a preview URL and the embed snippet.
curl -sX POST https://app.excuseme.app/api/v1/manage/surveys \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"type":"emojis","question":"How do you like the new dashboard?"}'
# -> {"surveyId":"...","status":"pending_confirmation","previewUrl":"https://app.excuseme.app/preview/...","embedSnippet":"<script src=\"https://hey.excuseme.app/excuseme.js?id=...\" defer></script>"}
# 3. Read results (summarize them yourself).
curl -s https://app.excuseme.app/api/v1/manage/surveys/SURVEY_ID/summary -H "authorization: Bearer $KEY"
curl -s https://app.excuseme.app/api/v1/manage/surveys/SURVEY_ID/responses -H "authorization: Bearer $KEY"
```
Endpoints: `POST /accounts`, `GET|POST /websites`, `GET|POST /surveys`,
`GET|PATCH|DELETE /surveys/{id}`, `GET /surveys/{id}/responses`,
`GET /surveys/{id}/summary`, `POST /surveys/{id}/upgrade` (returns a Stripe checkout URL).
Survey types: `emojis`, `stars`, `thumbs`. Errors are `{"error":{"code","message"}}`.
### Email check-in endpoints
Check-ins are recurring email surveys. Each send is a **wave** that rotates through the
check-in's question list. Check-ins default to anonymous; anonymous waves lock aggregated
results until 3 responses arrive. `sendWeekday` is 0 (Sunday) to 6 (Saturday).
**POST /checkins** — create a check-in.
```json
// request
{
"name": "Weekly team pulse",
"anonymity": "anonymous", // or "identified"
"frequency": "weekly", // "weekly" | "biweekly" | "monthly" | "manual"
"sendWeekday": 1, // required unless frequency is "manual"
"sendHour": 9, // 0-23; required unless frequency is "manual"
"timezone": "America/New_York", // IANA timezone; default "UTC"
"questions": [
{ "type": "emojis", "question": "How did this week go?" }
],
"recipients": [ // optional; up to 500
{ "email": "alice@example.com", "name": "Alice" }
]
}
// response 201
{
"checkinId": "uuid",
"publicId": "short-id",
"shareUrl": "https://app.excuseme.app/s/<publicId>",
"nextSendAt": "2024-01-08T09:00:00.000Z", // null when frequency is "manual"
"recipients": { "added": 1, "skipped": 0 } // null if none supplied at create
}
```
`400 recipient_limit` if the recipients list exceeds the plan cap (the check-in is still
created; `checkinId` is included in the error body). Send `Idempotency-Key` to make retries
safe.
**GET /checkins** — list all check-ins for the account. Returns an array of check-in objects.
**POST /checkins/{id}/recipients** — add recipients to an existing check-in.
```json
// request
{ "recipients": [{ "email": "bob@example.com", "name": "Bob" }] } // 1-500
// response 200
{ "added": 1, "skipped": 0 }
```
`400 recipient_limit` if the total would exceed the plan limit.
`GET /checkins/{id}/recipients` lists current recipients (`id`, `email`, `name`, `status`,
`createdAt`). `DELETE /checkins/{id}/recipients` with body `{"recipientId":"<uuid>"}` removes
one.
**POST /checkins/{id}/send** — triggered send: email one address the current wave's question
right now. Use this for CSAT-style flows (order shipped, ticket closed, etc.).
```json
// request
{ "email": "alice@example.com", "name": "Alice" } // name optional
// response 200
{ "sent": true, "waveId": "uuid", "recipientId": "uuid" }
```
Errors: `402 limit_reached` (check-in reached its free cap; upgrade first),
`409 recipient_blocked` (address bounced or unsubscribed; do not retry),
`409 already_responded` (recipient already answered in the current wave).
**GET /checkins/{id}/summary** — per-wave results, oldest wave first.
```json
{
"checkinId": "uuid",
"waves": [
{
"waveId": "uuid",
"question": "How did this week go?",
"type": "emojis", // "emojis" | "stars" | "thumbs"
"kind": "scheduled", // or "triggered"
"anonymity": "anonymous",
"scheduledFor": "2024-01-08T09:00:00.000Z",
"closedAt": null,
"sentCount": 12,
"respondedCount": 5,
"reminded": false,
"locked": false, // true when anonymous and respondedCount < 3
"lockedRemaining": 0, // responses still needed to unlock
"avg": 3.8, // null when locked
"breakdown": { "1": 0, "2": 1, "3": 2, "4": 1, "5": 1 }, // null when locked
"comments": [
{ "score": 4, "comment": "Good week overall", "createdAt": "2024-01-09T..." }
], // null when locked
"sources": { "email": 4, "link": 1 }
}
]
}
```
## Option B: the MCP server
`npx excuseme-mcp` exposes the same operations as MCP tools (`excuseme_create_survey`,
`excuseme_preview`, `excuseme_get_summary`, ...). Set `EXCUSEME_API_KEY` in its env.
See the package README for client config.
## How consent works
A new survey previews immediately but stores zero real responses until the owner clicks
the confirm link emailed at account or website creation. Show the human the `previewUrl`
in the same turn; they confirm once and collection starts.
## Pricing
Free: 1 active survey, 100 responses per survey. Paid: 1.99 USD or EUR per survey per
month for unlimited responses. Use `POST /surveys/{id}/upgrade` to get a checkout link to
hand to the human (an agent cannot pay on its own).