MailLandDocs

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) or s (Sandbox)
  • Product: e (Sending), i (Inbound), v (Validation)
  • Stream (Sending only): t (Transactional) or m (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 Validation

API 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:

PropertyTypeDescription
namestringHuman-readable label for this credential
productenumSending, Inbound, or Validation
modeenumProduction (delivers real emails) or Sandbox (does not deliver to ordinary recipients, see Sandbox Mode)
streamenumTransactional or Marketing (Sending product only)
channelsstring"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
scopeenumAllDomains or SelectedDomains
sandboxBehaviorenumLogOnly (default) or AllowListOnly (Sandbox mode only; API sends only)
rateLimitPerHourintMax emails per hour (default: 1000)
rateLimitPerDayintMax emails per day (default: 10000)
rateLimitPerMinuteintOptional per-minute limit
expiresAtdatetimeOptional 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

create-credential.sh
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
  }'
Response: 201 Created
{
  "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:

create-domain-scoped.sh
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:

update-domains.sh
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:

update-allowed-ips.sh
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:

ChannelUsage
ApiREST API via X-API-Key header
SmtpSMTP relay authentication (key as password)
Api, SmtpBoth 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:

rotate-key.sh
curl -X POST https://api.mail.land/v1/credentials/n4k9pq2rt8mwxa/rotate \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "gracePeriodHours": 24
  }'
Response: 200 OK
{
  "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 answered 202, but nothing is delivered and no message is created. The response has isSandboxed: true, sandboxReason: "credential_sandbox_mode" and an empty messageIds.
  • AllowListOnly: recipients that match your workspace's sandbox allow list are really delivered; every other recipient is returned in rejectedRecipients with the reason recipient_not_in_sandbox_allowlist. If no recipient is allowed, the response is still 202 with acceptedRecipients: [] and sandboxReason: "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

list-credentials.sh
curl "https://api.mail.land/v1/credentials?limit=20" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Filter by product or mode:

list-credentials-filtered.sh
curl "https://api.mail.land/v1/credentials?product=Sending&mode=Production" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Update a credential

patch-credential.sh
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

delete-credential.sh
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.

LimitDefaultDescription
Per hour1,000Stored on the credential; not currently enforced on the REST API send path
Per day10,000Stored 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: 45

Next Steps

On this page