Docs · API v1
Start here
How to connect a project to Loods: the hosts, your key, the conventions every call keeps, and the one shape every error has.
What Loods does
Loods reads the channels you name: mailboxes over IMAP, lead sources that push over HTTPS, and a business number on a messaging API. It posts every new message to your webhook as a signed JSON event.
On request, it reads a message or drafts a reply with an AI model that runs in the EU. It sends a chat message only when you ask for exactly that message, and it never sends mail.
Everything after that is yours: deciding, confirming, sending mail, keeping records, doing the sums.
Hosts
Staging is what you connect to today. It reads and sends for real; production is a name with a placeholder behind it until it is a service.
| Host | What it is |
|---|---|
https://loods.dev | Staging, and the one to connect to. It reads real mailboxes and sends real messages. |
https://loods.io | Production. A placeholder page until it is a service, so there is nothing to point at yet. |
Get a key
Keys are issued by hand: ask for one with an address that can be answered. The answer is a link that works once, and it runs out on the date it comes with. The page behind it shows two secrets, each shown once:
- an API key,
lds_…, which you send with every call; - a webhook secret,
whsec_…, which Loods signs every event with.
The key is made when you press the button, and Loods keeps only its hash: have somewhere to keep both before you do. The page makes one call, POST /v1/key-pickups, with the part of your link after the # as token. You can make it yourself.
A link that answers pickup_expired is asked for again. One that answers pickup_used when you did not use it means somebody else holds that key: say so, and it is revoked. A lost key is replaced the same way, with a new link. Your webhook secret stays as it was and is not shown again.
Send the key as a bearer token. GET /v1/me tells you who it belongs to and where your events go. A missing or wrong key is a 401 with the code invalid_api_key.
A key sees only its own client's channels and events. Anything else is a 404, as if it didn't exist.
Each client can make 600 requests a minute. Past that, you get a 429 with the type rate_limited.
/v1/mecurl https://loods.dev/v1/me \ -H "Authorization: Bearer $LOODS_KEY"
{ "id": "bouwbedrijf", "name": "Bouwbedrijf Voorbeeld", "webhook_url": "https://your.app/webhooks/loods" }
Conventions
- JSON in, JSON out. Send
Content-Type: application/json. - Times are ISO 8601 in UTC, like
2026-09-22T09:14:03Z. - Ids carry a prefix:
ch_for a channel,evt_for an event. - Channels have your names. Wherever a path takes a channel, its
refand its id both work. - Money is integers. Prices come back in cents and quantities in thousandths. There is never a total: you do the sums.
- New fields can appear in any answer or event. Ignore the ones you don't know.
Errors
Every error has the same shape, whatever went wrong.
typesays whose problem it is: your request, your key, or something upstream.codeis stable: branch on it.messageis for the person reading your logs, and may change.fields, on a422, lists what is wrong with each field you sent.
Every type and code is in the reference.
{ "error": { "type": "invalid_request", "code": "invalid_request", "message": "The settings.host field is required.", "fields": { "settings.host": ["The settings.host field is required."] } } }
Versions and the contract
Everything on these pages is /v1. A breaking change gets a new version beside it: /v1 is never changed out from under you.
With your key you get the OpenAPI spec and an example of every event and response. Loods tests its own output against those examples; copy them into your tests too.