API Keys
Create, configure, and manage API credentials with fine-grained access control
API keys (credentials) authenticate your application with the MailLand API. Each key is scoped by product, mode, stream, channel, and optionally restricted by domain and IP address.
In the dashboard, create and manage keys under Credentials in the sidebar (Global section). The same operations are available through the /v1/credentials API described below.
The Authorization: Bearer examples on this page use the JWT access token returned by POST /v1/auth/login (see Login). API keys are different: send them in the X-API-Key header, never as a Bearer token.
Key Format
All MailLand API keys follow this format:
ml_{mode}{product}{stream?}_{random45chars}ml_: prefix for all keys- Mode:
p(Production) ors(Sandbox) - Product:
e(Sending),i(Inbound),v(Validation) - Stream (Sending only):
t(Transactional) orm(Marketing) - 45 random characters
Examples:
ml_pet_Ab3Kx9mN2pQ7rT4wL8yD... → Production Sending Transactional
ml_sem_Xn7Kp2qR4tW9yB1cD6eF... → Sandbox Sending Marketing
ml_pi_Mn5Jp8kL3rV6wA2xC4fG... → Production Inbound
ml_pv_Qw9Er3tY6uI1oP5aS8dF... → Production ValidationAPI keys are shown only once at creation. Store them securely: MailLand stores only a hash of the key. If you lose a key, you'll need to rotate it or create a new one.
Credential Properties
When creating a credential, you configure the following:
| Property | Type | Description |
|---|---|---|
name | string | Human-readable label for this credential |
product | enum | Sending, Inbound, or Validation |
mode | enum | Production (delivers real emails) or Sandbox (does not deliver to ordinary recipients, see Sandbox Mode) |
stream | enum | Transactional or Marketing (Sending product only) |
channels | string | "Api", "Smtp" or "Api, Smtp" (a flags enum sent as one string; the integer value also works). Not a JSON array: determines where the key can authenticate |
scope | enum | AllDomains or SelectedDomains |
sandboxBehavior | enum | LogOnly (default) or AllowListOnly (Sandbox mode only; API sends only) |
rateLimitPerHour | int | Max emails per hour (default: 1000) |
rateLimitPerDay | int | Max emails per day (default: 10000) |
rateLimitPerMinute | int | Optional per-minute limit |
expiresAt | datetime | Optional expiration date |
channels is a single string, never an array. Send "channels": "Api, Smtp" to allow both channels; ["Api", "Smtp"] is rejected with 400.
Create a Credential
curl -X POST https://api.mail.land/v1/credentials \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production Transactional",
"product": "Sending",
"mode": "Production",
"stream": "Transactional",
"channels": "Api",
"scope": "AllDomains",
"rateLimitPerHour": 5000,
"rateLimitPerDay": 50000
}'{
"id": "n4k9pq2rt8mwxa",
"name": "Production Transactional",
"key": "ml_pet_Ab3Kx9mN2pQ7rT4wL8yD...",
"keyPrefix": "ml_pet_Ab3Kx9mN",
"product": "sending",
"mode": "production",
"stream": "transactional",
"channels": "api",
"scope": "allDomains",
"sandboxBehavior": "logOnly",
"rateLimitPerHour": 5000,
"rateLimitPerDay": 50000,
"isActive": true,
"createdAt": "2026-02-17T10:00:00Z"
}The key field is only included in the creation response. Copy it immediately.
Scope: Domain Restrictions
By default, credentials can send from any verified domain in your workspace (AllDomains). To restrict a credential to specific domains:
curl -X POST https://api.mail.land/v1/credentials \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Marketing - notifications.com only",
"product": "Sending",
"mode": "Production",
"stream": "Marketing",
"channels": "Api",
"scope": "SelectedDomains",
"domainIds": ["q7wm3nx9k2rtpz", "h5vy8jc2lspqwr"]
}'You can also update domain assignments after creation:
curl -X PUT https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa/domains \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"domainIds": ["q7wm3nx9k2rtpz", "h5vy8jc2lspqwr"]
}'IP Restrictions
Restrict a credential to specific IP addresses or CIDR ranges:
curl -X PUT https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa/allowed-ips \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"allowedIps": [
{ "ipAddress": "203.0.113.10", "description": "Production server" },
{ "ipAddress": "198.51.100.0/24", "description": "Office network" }
]
}'When IP restrictions are configured, API requests from unlisted IPs return 403 IP_NOT_ALLOWED.
Channel Configuration
The channels field controls where a credential can authenticate:
| Channel | Usage |
|---|---|
Api | REST API via X-API-Key header |
Smtp | SMTP relay authentication (key as password) |
Api, Smtp | Both channels |
To use a credential for SMTP relay, ensure Smtp is included in channels. When authenticating via SMTP, use any username and the API key as the password.
Key Rotation
Rotate a credential's API key without downtime using a grace period:
curl -X POST https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa/rotate \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"gracePeriodHours": 24
}'{
"key": "ml_pet_Yn8Lq3rS5uW0zA4bC7dE...",
"oldKeyExpiresAt": "2026-02-18T10:00:00Z"
}During the grace period (default 24 hours), both the old and new key are valid. After the grace period, the old key stops working.
Key rotation is ideal for zero-downtime deployments. Deploy the new key to your application, then let the old key expire naturally.
Sandbox Mode
"Sandbox" means two different things, and they combine:
1. Sandbox credentials (mode: Sandbox). Use them for development, CI/CD and staging. What happens to a send depends on the credential's sandboxBehavior:
LogOnly(default): the request is validated and answered202, but nothing is delivered and no message is created. The response hasisSandboxed: true,sandboxReason: "credential_sandbox_mode"and an emptymessageIds.AllowListOnly: recipients that match your workspace's sandbox allow list are really delivered; every other recipient is returned inrejectedRecipientswith the reasonrecipient_not_in_sandbox_allowlist. If no recipient is allowed, the response is still202withacceptedRecipients: []andsandboxReason: "all_recipients_not_in_sandbox_allowlist".
Over SMTP, a sandbox credential never delivers: the message is accepted and recorded but not sent, whatever its sandboxBehavior.
2. Sandbox workspaces. A new workspace starts at the Sandbox verification level (see Workspaces). Until the workspace is verified, sending is limited to recipients on the sandbox allow list, even with a Production credential. Any other recipient is refused with 429 SENDING_BLOCKED ("Recipient '…' is not in sandbox allowlist. Add to allowlist or complete verification.").
Manage the allow list in the dashboard under Settings → Sandbox Allow List. An entry is an address ([email protected]) or a whole domain (*@example.com). The same list is available as GET /v1/account/settings/sandbox-allowlist, POST /v1/account/settings/sandbox-allowlist, DELETE /v1/account/settings/sandbox-allowlist/{email} (dashboard bearer token; POST takes email and an optional description).
Managing Credentials
List credentials
curl "https://api.mail.land/v1/credentials?limit=20" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Filter by product or mode:
curl "https://api.mail.land/v1/credentials?product=Sending&mode=Production" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Update a credential
curl -X PATCH https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Name",
"rateLimitPerHour": 10000,
"isActive": false
}'All fields are optional: only include what you want to change.
Delete a credential
curl -X DELETE https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Returns 204 No Content on success. Deleted credentials immediately stop authenticating. Deletion revokes the key: the credential stays in GET /v1/credentials with isActive: false.
Rate Limits
Each credential stores its own rate limit values (per hour, per day, per minute, per batch), and they're validated on save. On the REST API send path today, these stored values are informational only: the API actually limits sending per workspace, using rates derived from your plan (see Rate Limits). There is no known consumer of the per-credential values on that path.
| Limit | Default | Description |
|---|---|---|
| Per hour | 1,000 | Stored on the credential; not currently enforced on the REST API send path |
| Per day | 10,000 | Stored on the credential; not currently enforced on the REST API send path |
| Per minute | - | Optional, not set by default |
| Per batch | - | Optional, max recipients per batch call |
When your workspace's plan-derived rate limit is exceeded, the API returns 429 with Retry-After (seconds to wait):
Retry-After: 45Next Steps
- Quickstart: send your first email
- REST API: full sending API reference
- SMTP Relay: authenticate with your API key via SMTP