Skip to content

Replies land back in your app.

A customer answers a receipt or a notification and the reply lands on the same conversation your app already has. Support tooling reads the history instead of starting cold.

( ONE THREAD, WHOLE HISTORY )

A reply lands on the thread it answers.

Outbound sends, replies, and the next outbound message all carry the same conversation id. A signed event tells your app when a new reply arrives, and the conversation object is what you fetch to see the whole exchange.

-> outbound "Your order shipped"     msg_7q2xk9mvt4znw8rh
<- reply "Which day exactly?"     msg_01hx2p7d
-> outbound "Thursday, by noon."     msg_01hx9r4k

conversation {
  id: "conv_01hx3f",
  contact: "ada@example.com",
  messages: 3
}
POST /webhooks/samva
webhook-id: whevt_7q2xk9mvt4znw8rh

{ type: "message.received",
  timestamp: "2026-07-08T09:42:14Z",
  data: { direction: "inbound",
    conversationId: "conv_k9mvt4znw8rh7q2x" } }
( REPLIES TODAY )

A reply shouldn't mean starting from zero.

Thread context breaks between email and your helpdesk.

A customer's reply arrives as a fresh email with no link to the receipt or notification it answers, so your team pieces the history back together by hand.

You end up writing an inbound parser.

Raw MIME, multipart bodies, and attachment extraction turn into a small project of their own before a single reply reaches your app.

Reply chains break.

A forward or a bounce drops out of the thread it belongs to, and your team loses the conversation exactly when a customer is already frustrated.

( HOW IT WORKS )

Parsed, threaded, delivered.

#01PARSE

Inbound is parsed for you.

MX records on your domain route incoming mail to Samva. Samva parses each raw MIME email into headers, body, and attachments. You write no parser.

MX support.example.com -> samva

parsed {
  from: "ada@example.com",
  subject: "Re: Your order shipped",
  hasAttachments: false
}
#02THREAD

Replies group into one conversation.

Replies, forwards, and bounces carry a conversationId that ties them to the send they answer. Threading is the data model, so you keep no thread id column of your own.

-> outbound "Your order shipped"
<- reply "Which day exactly?"

conversation.id: "conv_01hx3f"
# one id, every reply, forward, and bounce
#03FETCH

Fetch the thread by id.

A signed message.received event lands at your endpoint with the conversation id. Fetch the full thread from the conversations API whenever your app needs it.

GET /v1/conversations/conv_01hx3f

{ id: "conv_01hx3f",
  contact: "ada@example.com",
  messages: 3 }
( INBOUND, SEND, WEBHOOKS )

Wire up inbound from here.

Questions from support and CX leads.

How does threading work?

Every inbound email carries a conversationId that ties it to the outbound send it answers. Replies, forwards, and bounces all land on the same conversation, so you fetch one thread instead of reconciling separate emails.

What about attachments on inbound?

Samva parses each inbound email into headers, body, and attachments. The message.received event flags whether the email carried attachments, and you fetch the files themselves through the attachments API.

How do I reply from my app?

Call messages.send() and address the contact directly, threading the reply into the existing conversation. The response reaches the customer from your app instead of a shared inbox someone has to check.

How does routing work across domains?

Each domain you verify can have inbound receiving turned on, and you can register more than one webhook endpoint. Subscribe each endpoint to message.received so inbound for a support domain routes to a different service than inbound for your product domain.

Inbound

Let the next reply land in your app.

Verify a domain, turn on receiving, and register your endpoint.

Related Resources