Verification codes

Your agent signs up. The code is already out.

Give your agent an address of its own. When the service emails a code, one call waits for it and hands it back, already pulled out of the message. On every plan, including Free.

How it works

Three steps, one of them a wait.

The agent fills in the form. Pidgeon does the part it usually gets stuck on.

  • An address of its own

    One call creates it, on our shared domain or your own. The agent signs up with it and keeps it, so the login codes, the reset and the receipts that follow arrive there too.

  • One call that waits

    GET /v1/messages/await holds open for up to 280 seconds and answers when the email arrives. No polling loop to write.

  • The code, already out

    extracted.codes, most likely first, and extracted.links with the confirmation link at the top. No regex for whichever format this service uses.

Use it

Wait before you submit.

Take a cursor, submit the form, then wait for what arrives after it.

Building on an agent framework? The MCP server has await_message, and the SDKs and the CLI have the same call.

TypeScript
// Take a cursor before you submit the form, so an older email
// cannot be taken for the code you are about to cause.
const { data: before } = await messagesAwait({
  query: { identity_id: identityId, after: 'now', timeout: 1 },
  throwOnError: true,
});

await submitSignupForm(address);

const { data } = await messagesAwait({
  query: { identity_id: identityId, has_code: 'true', after: before.data.cursor, timeout: 120 },
  throwOnError: true,
});

const code = data.data.message?.extracted.codes[0]?.value;

The right code

Not just the nearest number.

Finding a code in an email is a guess. A careless guess is a security problem.

  • reply_text

    Only the new text

    Codes are read from what this email says, never from quoted history, so a code from an earlier message in the thread is not offered again.

  • from

    A forged sender is not the service

    Anyone can type no-reply@github.com into a From line. When the agent waits for a sender, only mail that passed DMARC counts.

  • held_back

    Nothing goes missing quietly

    A match filed as spam, or one that failed authentication, comes back as a count with its id, so "never came" and "came and was held" look different.

  • firewall

    A verdict on every message

    Each email comes with a Pidgeon Firewall verdict naming hidden text and instructions addressed to an AI, so the agent can check it before acting on the code.

See it run

An agent that signs itself up.

signup-agent creates its own address, fills in a sign-up page, types the code in, then gets the password reset at the same address. It runs in our CI.

Browser Use and the Pidgeon Python SDK, on GitHub. The walkthrough is in the blog post.

Questions

Asked before wiring it in.

How does an AI agent get a verification code from email?

It signs up with a Pidgeon address of its own, then calls GET /v1/messages/await on that address. The request waits until the email arrives, up to 280 seconds, and returns the message with the code already extracted in extracted.codes.

Does it work with one-time passwords and magic links?

Yes. Numeric and alphanumeric codes come back in extracted.codes, most likely first, and verification, confirmation and sign-in links in extracted.links, with unsubscribe and social links left out.

Why not a throwaway inbox?

A throwaway catches the first code. The account your agent just made keeps emailing: the next login code, a password reset, a receipt. With an address of its own, those arrive where the agent can read them.

Can my agent use it over MCP?

Yes. The hosted MCP server has await_message with the same parameters. The TypeScript and Python SDKs and the CLI have the wait too.

What if the code never arrives?

The wait returns timed_out with a cursor, so the next call misses nothing that arrives in between. If a matching email was filed as spam or failed authentication, held_back counts it and gives its id.

Is it on the free plan?

Yes. Three addresses, your own domain and the wait are on Free. Thirty waits a minute per workspace, on every plan.

Give your agent an address.

Paste one prompt, or make the call yourself. It is free to start.

Any agentprompt

Read https://pidgeon.ai/skill.md and follow the instructions to get your own inbox.

The full API reference is at https://pidgeon.ai/openapi.json

SignupTwo calls, and one code emailed to you