Mailan
Sign in

API basics.

Mailan has two halves and two kinds of credentials. Submissions come in through a public endpoint that anybody may call with your form's access key. Everything that reads data back needs a secret API key. This page covers what both halves share: the base URL, the response shape, status codes and the limits.

Base URL

Every API route sits below /api. On the hosted service that is https://mailan.email/api; on your own installation it is your domain with the same path. The health check is the one exception — it lives at the root, outside the prefix, so an uptime monitor does not have to know about /api.

bash

Access key and API key

The two are not interchangeable. The access key identifies one form and may sit in the HTML of a public page — it can only ever create a submission for that one form. The API key belongs to your account, reads your data, and must stay on a server.

Property Access key API key
Scope One form The whole account
Secret No — it is public by design Yes — treat it like a password
Sent as URL segment or access_key field Authorization: Bearer header
Allows Creating a submission Reading forms and submissions
Where to find it Dashboard → form → settings Dashboard → API keys

An API key is shown exactly once, right after you create it: only its hash is stored, so it cannot be displayed again. Keys look like mln_live_… and can be revoked at any time; a revoked key answers 401 like an unknown one.

bash

Response shape

Everything under /api/v1 answers in the same envelope: a success flag plus the payload in data. Errors carry the flag and a message instead. The submit endpoint is deliberately flatter — it answers a browser as well as a program, and returns success and message directly.

JSON

Status codes

Read the status code, not the message: the wording is meant for humans and may change, the codes will not.

Code Meaning What to do
200 Accepted or read successfully. Nothing.
303 Submission stored, browser is redirected to the target. Only for classic form posts. Send Accept: application/json to get JSON instead.
400 The request body could not be read or a value was invalid. Check content type and field names.
401 Missing, unknown or revoked API key. Check the Authorization header; create a new key if needed.
403 Captcha failed, origin not allowed, or the form is blocked. See the message; check the form's allowed domains.
404 Unknown access key, or a submission that is not yours. A foreign resource looks exactly like a missing one — that is intended.
413 An upload exceeded the size limit. Check the file before sending; the server aborts mid-stream.
429 Rate limit or monthly quota exceeded. Back off and retry later. Do not retry a submission blindly.
500 Something went wrong on our side. Retry with backoff; the submission may still have been stored.

Rate limits and quotas

Three limits exist side by side. Two protect the submit endpoint, one the read API; on top of them sits the monthly submission quota of your plan. The numbers below are the defaults of the hosted service — on your own installation they are environment variables.

Limit Default Environment variable
Submissions per form and minute 20 RATELIMIT_SUBMIT_PER_MINUTE
Submissions per IP and minute 10 RATELIMIT_SUBMIT_PER_IP_PER_MINUTE
Read API requests per key and minute 120 RATELIMIT_API_PER_MINUTE
Monthly submissions According to your plan Plan setting

Exceeding any of them answers 429. The submit limits use a sliding window, so the budget refills continuously instead of all at once on the minute.

Allowed domains

A form can be restricted to the domains it is embedded on. With the list empty every origin is accepted; as soon as it has entries, a submission from anywhere else is answered with 403. The same list also decides which redirect targets are accepted — that is what keeps the endpoint from becoming an open redirect.

Server-to-server requests send no Origin header and are therefore never blocked by this — the restriction protects against a copied form on a foreign page, not against a program.