Developer documentation
Test and Live projects
Every PulsePigeon project is either a Test project that captures sends, or a Live project that delivers them. This page explains how the modes differ and where the bypasses end.
What Test mode does
The normal message API returns a captured marker (delivery_mode=test_capture) for every Test project send. The message is stored for inspection, never handed to a provider, and never reaches a real inbox. Use a Live project with a verified domain to deliver real email.
Where the bypass applies
On the normal message API, Test mode skips three gates that Live mode enforces:
- Account review (vetting): Test projects accept sends before the workspace has passed the production review.
- Sender domain verification: Test projects accept an unverified From address.
- Billing: Test projects do not consume plan, contact, or daily caps.
The template Test-send route (POST /v1/templates/{template_id}/test-send) is the one exception: it requires the account review in every mode, including Test. It applies the normal recipient, content, and compliance gates. On a Live project it queues a real delivery to caller-chosen recipients; on a Test project the send is still captured, not delivered.
Other gates are enforced in every mode. Send control (workspace sending status or a safety restriction), recipient suppression, recipient consent, marketing content requirements, and content scanning all still apply on a Test project.
Test versus Live, side by side
| Area | Test | Live |
|---|---|---|
| Mode | Test | Live |
| What the API does | Accepts the send, captures it for inspection, and never reaches a real inbox. | Accepts the send and hands it to a configured provider for real delivery. |
| Vetting | Bypassed on the normal message API. The template Test-send route is the one exception: it requires the account review in every mode. | Required. The API returns 428 until the workspace has passed it. |
| Sender domain | Bypassed on the normal message API. Sends may use an unverified address. | Required. The From address must be on a verified domain or a confirmed single sender. |
| Billing | Bypassed on the normal message API. Plan and contact caps do not trigger. | Enforced. Monthly, contact, and daily caps are rejected before acceptance. |
| Send control | Enforced in every mode. Workspace sending status and safety restrictions still apply. | Enforced in every mode. |
| Recipient gates | Enforced in every mode. Suppressed, deleted, opted-out, or non-consented recipients are still refused. | Enforced in every mode. |
| Content scanning | Enforced in every mode. | Enforced in every mode. |
| Marker | delivery_mode=test_capture | delivery_mode omitted |
Keys, consent, and mode
Mode is fixed at project creation. You cannot change a project from Test to Live, or from Live to Test. The bound credentials and consent decisions follow the same rule.
| Credential or flow | Rule |
|---|---|
| API key | A project API key is bound to one project and one mode: a Test project key cannot send from a Live project, and a Live project key cannot send from a Test project. A workspace key with access to all environments can send to Test and Live projects in its workspace, and the project's mode then decides capture or delivery. Recreate a project key for the project and mode you intend to use. |
| OAuth consent | An OAuth consent for a connected AI app selects one project and one mode at the time the user grants access. Switching project or mode requires starting the connection again. |
| Project mode | A project's mode is fixed at creation. Move sends to a different project to use a different mode; you cannot change the mode of an existing project. |
Send conditions catalog
The HTTP status, typed code, reason, and fix for every entry in the catalog's send group. The same entries drive the support block returned with each API error.
| HTTP | Code | Why | Fix |
|---|---|---|---|
| 403 | permission_denied | Your role is Viewer, which is read-only. | Ask an owner or admin for the Developer role or higher. |
| marker | delivery_mode=test_capture | This project is in Test mode, so sends are captured and never reach a real inbox. | Send from a Live project with a verified domain to deliver real email. More |
| 403 | api_key_environment_mismatch | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. More |
| 403 | api_key_project_mismatch | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. More |
| 422 | api_key_environment_project_mismatch | This API key belongs to a different project or mode than the one you used. | Create a key in the same project and mode, then use it. More |
| 403 | domain_not_verified | The From address is not on a domain you have verified. | Verify the domain, or confirm this single sender address, then send again. More |
| 428 | pre_send_vetting_required | Production sending starts after your account passes a short review. | Finish the review, or contact support if it is stuck. More |
| marker | template_test_send_vetting | Template test sends need the account review even for Test projects. | Finish the account review, or send a test through the message API to a Test project. More |
| 423 | sending_halted | Sending is paused for this workspace. | Read the reason shown, then contact support to appeal. |
| 402 | subscription_restricted | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. |
| 402 | subscription_suspended | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. |
| 402 | subscription_canceled | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. |
| 402 | subscription_quarantined | Your subscription state blocks new sends. Exports stay available. | Fix billing to resume sending. |
| 402 | plan_monthly_limit_exceeded | Your plan's monthly email or active contact limit is used up. | Upgrade your plan or wait for the next period. |
| 402 | plan_contact_limit_exceeded | Your plan's monthly email or active contact limit is used up. | Upgrade your plan or wait for the next period. |
| 429 | hard_spend_cap_exceeded | Your daily sending cap is reached. | Retry after the daily reset or review your limits. |
| 429 | sending_limit_exceeded | Your daily sending cap is reached. | Retry after the daily reset or review your limits. |
| 403 | recipient_unsubscribed | This recipient opted out of this kind of email. | Do not resend to this address in this category. More |
| 422 | recipient_suppressed | A recipient is suppressed or was deleted. | Remove that recipient from the send. More |
| 422 | recipient_deleted | A recipient is suppressed or was deleted. | Remove that recipient from the send. More |
| 422 | recipient_suppressed_by_dsr | A recipient is suppressed or was deleted. | Remove that recipient from the send. More |
| 422 | consent_required | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. |
| 422 | physical_address_required | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. |
| 422 | can_spam_footer_required | Marketing mail needs recipient consent. Set mailing address under /compliance. | Save your mailing address on /compliance, then record consent for recipients. |
| marker | batch_item_failed | An item in the batch failed; others may still have been accepted. | Read that item's error; resend unchanged failed items with their original key, and use a new key for a corrected message. More |
| marker | delivery_pending | The message is queued and not yet handed to a provider. | Wait; no action needed. More |
| marker | delivery_deferred | The provider reported a delay for this recipient. | Check the delivery timeline and Bounces before you resend. More |
| marker | delivery_bounced_suppressed | The address bounced, so it is suppressed and future sends to it are refused. | Fix or remove the address; a suppressed address is not retried. More |
| marker | delivery_failed_local | PulsePigeon could not hand the message to a provider because of a delivery or configuration problem on our side or in your setup. | Check the failure reason on the message; contact support if it repeats. More |
| 503 | recipient_gate_unavailable | A recipient eligibility check could not be completed. | Check whether the send was accepted before retrying; if it was not, try again later. |
| 403 | recipient_suppressed_global | A workspace suppression blocks this recipient. | Review the workspace suppression before attempting another send; a stream change will not remove it. |
| 403 | recipient_suppressed_stream | An active suppression for this stream blocks the recipient. | Review the stream suppression and recipient preference before attempting another send. |
| 503 | stream_suppression_unavailable | The stream suppression service could not complete a required check or token operation. | Check whether the operation was accepted before retrying; if its outcome is unknown, verify its current state first. |
| 503 | unsubscribe_origin_unavailable | The hosted unsubscribe origin is unavailable for this send. | Check whether the send was accepted before retrying; contact support if link generation remains unavailable. |
| 422 | transactional_content_promotional | Transactional, security, and billing mail cannot include promotional phrasing. | Send this as category=marketing with real recipient consent, or remove the promotional phrasing. More |
| 503 | unsubscribe_link_unavailable | The unsubscribe link for this marketing send could not be minted. | Retry the send. Contact support if link generation remains unavailable. |
| 422 | marketing_footer_too_large | The marketing footer would push this HTML body past the allowed size. | Shorten the HTML body and retry the send. |
Related guides
Why didn't my email arrive · API error reference · Projects · API keys