Concepts
Core concepts behind OneShotMail -- addresses, modes, TTL, labels, credits, and the one-shot guarantee.
Addresses
An address is the fundamental unit in OneShotMail. Each address is a randomly generated, cryptographically unique email address. Receive addresses use @in.oneshotemail.com, send addresses use @out.oneshotemail.com:
abc123xyz789def456@in.oneshotemail.com # receive mode
abc123xyz789def456@out.oneshotemail.com # send mode
The local part (before the @) is a 20-character base62 string generated using secrets.token_urlsafe. It cannot be guessed or enumerated.
When you create an address, you get back an address ID (the same as the local part) that you use for all subsequent operations — checking status, retrieving the email, or deleting the address.
Modes
Every address operates in exactly one of two modes:
Receive mode
The default. A receive-mode address waits for exactly one inbound email.
Lifecycle: waiting —> received —> expired
waiting— The address exists and is ready to accept an email. No email has arrived yet.received— An email has been delivered. The email content is available via the API. Only the first email is kept; subsequent emails to the same address are silently discarded.expired— The TTL has elapsed. The address and all associated data (email content, attachments) have been permanently deleted.
Send mode
A send-mode address sends exactly one outbound email and then self-destructs.
Lifecycle: pending —> sent —> expired
pending— The address has been created and the email is queued for sending.sent— The email has been accepted by SES for delivery. Delivery status (delivered, bounced, complained) is tracked in the address metadata.expired— The TTL has elapsed and the address record has been cleaned up.
Send mode costs more credits than receive mode because it incurs SES sending costs and requires domain reputation management.
TTL (Time-to-Live)
Every address has a TTL that determines how long it exists before auto-deletion. You set the TTL when creating the address:
# Lives for 5 minutes
addr = oneshot.create(ttl_seconds=300)
# Lives for 1 hour (the default)
addr = oneshot.create(ttl_seconds=3600)
TTL limits depend on your plan:
| Plan | Max TTL |
|---|---|
| Free | 1 hour |
| Solo | 12 hours |
| Team | 24 hours |
| Enterprise | Custom |
| Max | 24 hours |
When the TTL expires:
- The address stops accepting email.
- The address record and all email data are permanently deleted.
- There is no recovery.
The One-Shot Guarantee
This is the defining feature of OneShotMail. Each address accepts exactly one email. When the first email arrives:
- The address status changes from
waitingtoreceived. - The email is stored.
- All subsequent emails to that address are silently discarded.
This guarantee is what makes OneShotMail perfect for automated testing. Each test gets its own isolated address. There is no cross-contamination between test runs, no leftover emails from previous executions, and no race conditions from shared inboxes.
If an address is in received status and another email arrives for it, the second email is dropped without any bounce message. The sender receives no indication that the email was discarded. This prevents information leakage about which addresses exist.
Labels
Labels are optional, user-defined strings you attach to addresses when creating them. They are the primary mechanism for organizing and bulk-managing addresses in test suites.
# Tag addresses with the CI run ID
run_id = os.environ.get("CI_RUN_ID", "local")
addr1 = oneshot.create(label=f"ci-{run_id}")
addr2 = oneshot.create(label=f"ci-{run_id}")
addr3 = oneshot.create(label=f"ci-{run_id}")
# After the test run, clean up all addresses in one call
oneshot.delete_by_label(f"ci-{run_id}")
Labels support filtering in the list endpoint:
# Find all addresses from this test run
addresses = oneshot.list(label=f"ci-{run_id}")
Best practices for labels:
- CI/CD runs: Use the build ID, commit SHA, or run number. Example:
ci-run-a1b2c3d4. - Test suites: Use the test class or feature name. Example:
signup-flow. - Manual testing: Use a descriptive name. Example:
debug-invoice-email. - Cleanup: Always call
delete_by_label()in your test teardown or CI cleanup step.
Credits
OneShotMail uses a credit system for usage beyond your plan’s monthly allocation.
How billing works (in order)
- Monthly allocation — Your plan includes a set number of receive and send operations per month. These are used first.
- Credit balance — If your monthly allocation is exhausted, credits are consumed instead. 1 credit = 1 receive address creation. Send operations consume more credits (see your plan details).
- Quota exceeded — If both your allocation and credits are exhausted, the API returns a
402error with a link to upgrade or purchase more credits.
Purchasing credits
Credits are sold in packs and are available on every plan at the same price. Credits are always more expensive per address than your plan allocation, so if you’re buying credits regularly, upgrading your plan saves money. See Billing & Usage for full pricing.
Credits expire 12 months after purchase. Unused credits carry over between billing cycles (they do not reset monthly).
Credit costs by operation
| Operation | Credit cost |
|---|---|
| Create (receive) | 1 credit |
| Create + send | 2 credits |
API Keys
Your API key is the credential used to authenticate all API requests. It has the format:
osm_live_aBcDeFgHiJkLmNoPqRsT...
Key facts about API keys:
- Shown once. When you register or regenerate your key, you see it exactly once. Store it securely.
- Stored as a hash. OneShotMail stores only a bcrypt hash of your key. We cannot retrieve it for you.
- Prefix-indexed. The first 8 characters are stored separately for fast lookup. The
osm_live_prefix identifies it as a live key. - Environment variable. All SDKs and the CLI read from the
ONESHOT_API_KEYenvironment variable by default. - One key per account. Regenerating your key invalidates the previous one immediately.
Plans
| Plan | Receive/mo | Send/mo | Max email size | Max TTL | Rate limit |
|---|---|---|---|---|---|
| Free | 25 | 5 | 1 MB | 1 hour | 5 rps |
| Solo | 10,000 | 1,000 | 10 MB | 12 hours | 50 rps |
| Team | 100,000 | 10,000 | 25 MB | 24 hours | 200 rps |
| Enterprise | 500,000 | 50,000 | 50 MB | 24 hours | 500 rps |
| Max | 1,000,000 | 100,000 | 50 MB | 24 hours | 1,000 rps |
See the Billing guide for full details on plans, credits, and the free tier queue.