Linje

API docs

Send transactional email. Receive incoming email as webhooks.

Linje handles email in both directions. Your app can send product email with a simple API call, and inbound mail can come back into your application as a signed webhook.

Send mail POST /v1/messages
Create inbox POST /v1/inboxes
Admin contract GET /admin/openapi.json

Auth

Three token scopes.

Token Use Scope
Admin token Provision projects, domains, setup, admin APIs Full control plane
Project token Send mail, manage project inboxes One project
Inbox manage token Inspect or manage one inbox One inbox

Send email

Use POST /v1/messages for transactional mail.

This is the core send endpoint. It is intended for receipts, magic links, password resets, invites, and other product mail.

curl -fsS \
  -H "Authorization: Bearer $LINJE_PROJECT_TOKEN" \
  -H "content-type: application/json" \
  -H "idempotency-key: magic-link:user-123" \
  -d '{
    "from":"auth@tx.example.com",
    "to":["you@example.com"],
    "subject":"Your sign-in link",
    "text":"Use the sign-in link in your app.",
    "html":"<p>Use the sign-in link in your app.</p>"
  }' \
  https://api.linje.systems/v1/messages
Auth

Use a project token for normal sending.

Idempotency

Set an `idempotency-key` for safe retries.

From domains

Project-token sends must use an allowed `from` domain.

Inbound email

Use POST /v1/inboxes to turn email into webhooks.

You can provision a Linje inbox, point mail directly at it, or forward mail into it. When Linje receives a message, it POSTs a structured JSON payload to your endpoint.

curl -fsS \
  -H "Authorization: Bearer $LINJE_PROJECT_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "id":"support",
    "webhook-url":"https://app.example.com/inbound"
  }' \
  https://api.linje.systems/v1/inboxes
POST https://app.example.com/inbound
x-linje-inbound-id: ...
x-linje-signature: sha256=...

{
  "event-id":"...",
  "from":"user@example.com",
  "to":"support@in.example.com",
  "subject":"Need help",
  "attachments":[...]
}

Signatures

Webhook signatures use HMAC-SHA256 over timestamp + body.

Verify against the raw request body bytes, not re-serialized JSON. Linje sends `x-linje-timestamp` and `x-linje-signature` on inbound and outbound webhooks.

expected = HMAC_SHA256(secret, timestamp + "." + raw_body)
x-linje-signature = "sha256=<hex>"
Inbound headers

`x-linje-inbound-id`, `x-linje-delivery-id`, `x-linje-timestamp`, `x-linje-signature`

Delivery model

Webhook delivery is at least once, so your endpoint should be idempotent.

Projects and domains

Projects give you scoped tokens and sender-domain rules.

Admins can provision projects and create sender domains. Project tokens are then safe to use from your application for normal message sending.

curl -fsS \
  -H "Authorization: Bearer $LINJE_API_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "id":"acme",
    "from-domains":["tx.acme.com"]
  }' \
  https://api.linje.systems/v1/projects/provision
Create sender domains

`POST /v1/domains` returns DNS records for verification.

Update limits

`PATCH /v1/projects/:id` can update rate limits, daily limits, and webhooks.

Outbound event webhooks

Projects can receive delivery lifecycle events too.

If a project has an outbound webhook configured, Linje can emit `message.accepted`, `message.delivered`, `message.bounced`, and `message.dead` events.

Event Meaning
message.accepted Linje handed the message off successfully.
message.delivered Remote MX accepted the message.
message.bounced A DSN bounce was ingested and correlated.
message.dead Linje stopped retrying delivery.

Reference

Key endpoints.

Method Path Purpose
POST /v1/messages Send transactional email
GET /v1/messages/:id Inspect one message
POST /v1/inboxes Create or update an inbound inbox
GET /v1/inboxes List inboxes
POST /v1/projects/provision Provision a project and issue a project token
PATCH /v1/projects/:id Update project settings and limits
POST /v1/domains Create a sender domain and get DNS records
GET /admin/openapi.json Machine-readable admin API contract
POST /admin/setup Provision one integration from the admin API