Search the docs

Find a page, a section, or an endpoint.

Test mode

A key whose mail never leaves Pidgeon, for CI.

A test key (pgn_test_…) does everything a live key does, and none of its mail leaves Pidgeon. Use one in CI, in a staging environment, or anywhere an agent is being built and should not be writing to real people.

What runs, and what does not

Everything runs: validation, the send checks (who may send as which address, suppression, the address's send policy), threading, the stored message, events and their webhooks, receipts, Idempotency-Key, and the Firewall on mail that arrives.

What does not:

  • Nothing is handed to the mail provider. Not for a send, a reply, a forward, a draft sent later, a scheduled send, or a calendar invitation. Whatever the recipient, whichever route or retry.
  • Nothing counts. No daily sending allowance, no usage, no billing. The five-minute and per-minute limits still apply, at your plan's numbers, but in a bucket of their own. A test suite cannot use up what your production server needs.
  • No person is notified by email or push about test mail.

Every message written in test mode has "test": true — on the message, on the answer to a send, and on message.sent and message.received.

Making one

Pass test: true when you mint the key. A test key's own children are always test keys.

curl https://pidgeon.ai/api/v1/api-keys \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "CI", "identity_ids": ["identity-id"], "test": true }'

A test key works on the addresses it was given. It cannot create or delete addresses, domains or pods, or issue IMAP and SMTP passwords: each of those counts against your plan, reaches the provider, or opens a way to send that test mode does not cover. Make the address with a live key, then mint a test key for it.

Where test mail goes

A test send to one of your own account's addresses is delivered there, marked test, so two of your agents can talk to each other in CI. A test send to anybody else, including an address in another Pidgeon account, is accepted, stored as sent, and goes nowhere.

A reply to a test message is a test message too, whoever sends it. If a person answers a simulated customer from the dashboard, that reply also stays inside.

Playing the other side

Nobody outside will ever answer a test send, so POST /v1/test/inbound plays the other side. It delivers a message to one of the test key's addresses through the real inbound pipeline, exactly as an email from outside would arrive. Pass in_reply_to with a message id to have it arrive as a reply in that thread.

curl https://pidgeon.ai/api/v1/test/inbound \
-H "Authorization: Bearer $PIDGEON_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
  "to": "support@yourapp.pidgeon.email",
  "from": "customer@example.com",
  "subject": "Re: Your order",
  "text": "It never arrived.",
  "in_reply_to": "message-id"
}'
json
{
"data": {
  "status": "delivered",
  "message_id": "…",
  "thread_id": "…",
  "identity_id": "…",
  "spam": false
}
}
  • Test keys only. A live key gets 403: it would put a made-up sender into a real inbox.
  • An address the key reaches at send. Anything else is 404, the same as an address that does not exist.
  • The sender's rules, the spam filter and the Firewall all judge it. A sender rule that rejects from rejects it here, with status: "rejected".
  • Up to 1,000 an hour per account. It takes an Idempotency-Key, so a CI step that retries does not deliver twice.