Linje

Reply by Email API

Let customers reply by email without losing application context.

Give each order, claim, case, or workflow an opaque Reply-To address. When someone replies, Linje sends your application a signed event containing the original context, message content, and attachments.

Reply sidecar

Keep your current outbound provider.

Continue sending through Resend, SendGrid, Mailgun, SES, or your existing email stack. Put the Linje-generated address in Reply-To; Linje handles the inbound reply, correlation, signed delivery, retries, and replay. Moving outbound email to Linje is optional.

Provider guides

Keep the sender you already use.

Use cases

Keep email replies inside the product that owns the context.

Claims and orders

Attach a customer's answer to the claim or order that prompted it.

Support and cases

Turn the reply into a comment on the correct ticket or application case.

Documents

Receive requested files against the right application, claim, or review.

Why reply routes

Use an application abstraction instead of assembling correlation primitives.

Some email providers expose recipient tags or other inbound primitives. Linje manages the route, its immutable application context, and the delivery lifecycle as one project-scoped resource.

Generic inbound primitives Linje reply route Application result
Generate a token and persist its mapping Create a route with immutable metadata Context arrives in the event
Own token lifecycle, expiry, and lookup Manage one opaque route resource No business identifier in the address
Integrate provider-specific delivery semantics Receive a signed, replayable event One inspectable reply boundary

Route

Create a project-scoped reply address.

1. Create a generated inbox for your webhook

curl -fsS \
  -H "Authorization: Bearer $LINJE_PROJECT_TOKEN" \
  -H "content-type: application/json" \
  -d '{"webhook-url":"https://app.example.com/linje/inbound"}' \
  https://api.linje.systems/v1/inboxes/provision

Copy the returned inbox.id and store the one-time webhook secret.

2. Create a reply route for one application object

curl -fsS \
  -H "Authorization: Bearer $LINJE_PROJECT_TOKEN" \
  -H "Idempotency-Key: 018f47d2-89ab-4cde-8123-456789abcdef" \
  -H "content-type: application/json" \
  -d '{
    "inbox-id":"<returned-inbox-id>",
    "metadata":{
      "claim_id":"claim_123",
      "customer_id":"cus_17"
    }
  }' \
  https://api.linje.systems/v1/reply-routes
{
  "reply-route": {
    "id":"route_...",
    "address":"r+012345...@in.linje.systems",
    "metadata":{
      "claim_id":"claim_123",
      "customer_id":"cus_17"
    },
    "active":true
  }
}

Use a fresh UUIDv4 as the idempotency key for each logical route. Concurrent and sequential exact retries return the same route and address; conflicting reuse returns 409. After retention cleanup, a retry returns 410 so a purged address cannot be revived or rebound.

Outbound

Use the generated address as Reply-To.

From: claims@example.com
To: customer@example.com
Reply-To: r+012345...@in.linje.systems
Subject: Claim claim_123

These are ordinary email headers. The message can be sent through Linje or another transactional email provider.

Inbound

The reply arrives with caller metadata.

POST https://app.example.com/linje/inbound
x-linje-inbound-id: ...
x-linje-delivery-id: ...
x-linje-timestamp: ...
x-linje-signature: sha256=...

{
  "schema":"linje.inbound-email.v1",
  "inbound-id":"...",
  "inbox-id":"claims",
  "route": {
    "id":"route_...",
    "metadata":{
      "claim_id":"claim_123",
      "customer_id":"cus_17"
    }
  },
  "email": {
    "from":[{"email":"customer@example.com"}],
    "subject":"Re: Your order",
    "in-reply-to":["<linje...@tx.example.com>"],
    "references":[]
  },
  "content":{"text":"The requested photo is attached."},
  "attachments":[
    {
      "filename":"photo.pdf",
      "download":{"url":"https://api.linje.systems/..."}
    }
  ]
}

Lifecycle

Expire or disable addresses without changing metadata.

curl -fsS -X PATCH \
  -H "Authorization: Bearer $LINJE_PROJECT_TOKEN" \
  -H "content-type: application/json" \
  -d '{"active":false}' \
  https://api.linje.systems/v1/reply-routes/route_...

Disabled, expired, malformed, and unknown routes do not reach the target inbox webhook and do not expose caller metadata.

Route listings accept inbox-id, cursor, and limit. The default page size is 100 and the maximum is 500. Responses use next-cursor pagination.

The complete address is returned only on creation and exact idempotent retries, so store it when you create the route. Metadata is immutable and limited to 16 KiB.

Disabled and expired routes can be removed after the configured retention window; Linje retains only a minimal hash tombstone.

Inspect the complete OpenAPI 3.1 contract.

The complete product

Want one system for both directions?

Linje also sends transactional email, with idempotent enqueueing, retries, delivery events, bounce handling, suppression, and inspectable timelines. Keeping your current sender is an adoption option, not a product limitation.

See the transactional email API.

Boundary

Correlation is not workflow.

Linje tells your application which route received the email. Your application decides whether the message updates an order, creates a comment, or does nothing. Linje does not become a helpdesk, rules engine, or shared inbox.

Pricing

Free during preview. Paid plans start at $5/month.

Create an account and test the complete reply flow without entering payment details. Creating and keeping reply routes does not consume message usage. Each customer reply counts once when Linje accepts it; webhook retries and replay are included. After preview, choose 500 messages free, 5,000 for $5/month, or 50,000 for $25/month.

See pricing and usage details.

Self-service quickstart

Route one real reply back to the right object.

Keep your outbound provider, create one reply route, and verify that text and an attachment reach the correct application object through a signed webhook.

Add replies to one real email flow Read the API docs