Use when authenticating or authorizing requests to any Mailtrap API (Email Send, Email Testing/Sandbox, Templates, Contacts, Sending Domains, Suppressions). Use when picking the auth header, choosing token scope, storing tokens safely, or resolving the Mailtrap account_id. Use before writing or generating any Mailtrap API call.
9 stars
0 votes
0 copies
4 views
Added May 28, 2026
toolsgoshellbashawsgcpazuretestingdebugginggitapi
Works with
cli
api
Security analysis
C71/100
mediumUses curl or wget to download content
criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study
Installs into .claude/skills of the current project.
Are you the author of authorizing-api-requests?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mailtrap-authorizing-api-requests)
---
name: authorizing-api-requests
description: >-
Use when authenticating or authorizing requests to any Mailtrap API
(Email Send, Email Testing/Sandbox, Templates, Contacts, Sending Domains,
Suppressions). Use when picking the auth header, choosing token scope,
storing tokens safely, or resolving the Mailtrap account_id. Use before
writing or generating any Mailtrap API call.
---
# Authorizing Mailtrap API requests
## Overview
Every Mailtrap API request needs **two things**:
1. An **API token** in an auth header — proves identity and carries the scope.
2. For account-scoped endpoints (most of them outside of the send hosts), an **`account_id`** in the URL path.
This skill is the single source of truth for both. Other skills (`sending-emails`, `testing-with-sandbox`, `using-email-templates`, `managing-contacts`, `setting-up-sending-domain`) reference these conventions instead of duplicating them.
## When to use
- Before writing any Mailtrap API call from code, scripts, CI, IaC, or an AI agent
- Picking which token scope and stream to provision
- Deciding where to store a token (env, secret manager, CI)
- Resolving the `account_id` for an account-scoped endpoint
- Debugging `401 Unauthorized` / `403 Forbidden` responses
## API tokens
Create tokens at **Settings** > [API Tokens](https://mailtrap.io/api-tokens) with the **smallest scope** that works:
- **Email Sending API** — for `send.api.mailtrap.io` and `bulk.api.mailtrap.io`. Scope per stream (transactional, bulk) when possible.
- **Email Testing API** — for the Sandbox (`sandbox.api.mailtrap.io`). Always separate from live sending tokens.
- **Account-level API** — for Contacts, Templates, Sending Domains, Suppressions, and other endpoints under `https://mailtrap.io/api/accounts/{account_id}/...`.
A single token can cover several scopes if the user has the right plan; prefer narrower tokens (one stream / one project / one product surface) so a leak has limited blast radius. Reference: [API tokens documentation](https://docs.mailtrap.io/email-api-smtp/setup/api-tokens.md).
## Auth headers (two equivalent forms)
Mailtrap accepts either header. Use Bearer in examples — it's the more common HTTP convention and matches most generated SDK code.
| Form | Header | When to use |
| ------------------- | -------------------------------------------- | -------------------------------------------- |
| Bearer (preferred) | `Authorization: Bearer $MAILTRAP_API_TOKEN` | Default for new code, SDKs, curl examples |
| Api-Token (legacy) | `Api-Token: $MAILTRAP_API_TOKEN` | Older clients or where Bearer is awkward |
Do not send both at the same time. The same value goes in either header.
## Where to put tokens
- **Local dev:** environment variable, or `.env` file that is in `.gitignore`. Load with `direnv`, `dotenv`, or the framework's built-in mechanism.
- **CI / build:** the CI provider's encrypted secret store (GitHub Actions secrets, GitLab CI variables, CircleCI contexts). Inject as env vars only.
- **Production / staging:** a real secret manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, Doppler, 1Password, etc.). Rotate on a schedule.
- **Agent / LLM workflows:** the host agent's secret store. Never paste a token into chat or a prompt.
Hard rules:
- **Never** hardcode a token in source, config, or notebooks.
- **Never** commit a token. If one lands in git, rotate it; history retention is forever.
- **Never** pass a token on the command line as a flag — it leaks into shell history, `ps`, and CI logs.
- **Never** let an LLM echo a literal token back into generated code. Use `$VAR_NAME` shell-var placeholders in all examples so generated code reaches for the env var, not the literal.
- **Never** mix sandbox and live tokens. A leaked sandbox key must not be able to send real mail.
## Recommended env var names
These names are used consistently across every other skill in this repo and across the example snippets below.
| Variable | Used for |
| ---------------------------- | -------------------------------------------------------------------------------------- |
| `MAILTRAP_API_TOKEN` | General API: Email Send (transactional and bulk), Templates, Contacts, Sending Domains, Suppressions |
| `MAILTRAP_SANDBOX_API_TOKEN` | Sandbox / Email Testing (separate scope) |
| `MAILTRAP_ACCOUNT_ID` | Path parameter for account-scoped endpoints |
If your environment uses different names, alias them once at startup so the examples in other skills work unchanged.
## Resolving `account_id` automatically
`account_id` is the integer prefix on every `https://mailtrap.io/api/accounts/{account_id}/...` endpoint. **Do not hardcode it.** It changes between environments, is different per organization, and is silently wrong when you copy a script to a teammate's account.
Resolve it once per session from the Accounts endpoint, which lists every account the token can access:
```bash
curl -s https://mailtrap.io/api/accounts \
-H "Authorization: Bearer $MAILTRAP_API_TOKEN"
```
Response shape (array):
```json
[
{"id": 12345, "name": "My Company", "access_levels": [1000]},
{"id": 67890, "name": "Client Account", "access_levels": [100]}
]
```
`access_levels` values:
- `1000` — Account owner
- `100` — Admin
- `10` — Viewer (read-only on most endpoints)
One-liner to cache as an env var (pick the right account if the token can see more than one):
```bash
export MAILTRAP_ACCOUNT_ID=$(curl -s https://mailtrap.io/api/accounts \
-H "Authorization: Bearer $MAILTRAP_API_TOKEN" | jq '.[0].id')
```
Reference: [Accounts API](https://docs.mailtrap.io/developers/account-management/accounts).
## Quick reference
```bash
# Live sending (no account_id in path)
curl -X POST https://send.api.mailtrap.io/api/send \
-H "Authorization: Bearer $MAILTRAP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
# Account-scoped endpoint
curl "https://mailtrap.io/api/accounts/$MAILTRAP_ACCOUNT_ID/contacts/lists" \
-H "Authorization: Bearer $MAILTRAP_API_TOKEN"
# Sandbox / Testing
curl -X POST "https://sandbox.api.mailtrap.io/api/send/$MAILTRAP_INBOX_ID" \
-H "Authorization: Bearer $MAILTRAP_SANDBOX_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
```
## Common mistakes
| Mistake | Fix |
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Hardcoding the token in code, config, or a notebook | Load from `$MAILTRAP_API_TOKEN` (env, `.env`, CI secret, secret manager); rotate the token if it ever leaked |
| Passing the token as a CLI flag (`--token=...`) | Use env vars; CLI flags leak to shell history, `ps`, and CI logs |
| Committing a token, then deleting it in a later commit | History keeps the value forever — rotate the token immediately, do not just remove the file |
| Pasting a token into chat / prompt / issue | Treat chat as public; rotate if it happened |
| Using the live `MAILTRAP_API_TOKEN` against the sandbox host | Sandbox uses its own scope and `MAILTRAP_SANDBOX_API_TOKEN`; mixing them either fails or sends real mail by accident |
| Hardcoding `account_id` | Resolve via `GET https://mailtrap.io/api/accounts` once per run and pass through `$MAILTRAP_ACCOUNT_ID` |
| Picking the wrong account when the token can see several | Filter the `GET /api/accounts` response by `name` or `access_levels` (1000 = owner) instead of `.[0]` |
| Sending both `Authorization` and `Api-Token` headers | Pick one (Bearer for new code); duplicating them is unnecessary and confuses some intermediaries |
| Using a viewer-scoped token for writes | Check `access_levels`; writes need 100 (admin) or 1000 (owner) for the relevant account |