GuidesReplies to your emails
Turn replies to your emails into support conversations
Receipts, order updates and account notices get answered. Add one field to the emails your app already sends, and every reply opens a conversation in your support channel, linked to the email it answers.
Using an AI coding agent? Give it this.
Route replies to this app's emails into our Rasket support inbox.
Read the Markdown of Rasket's guide first: /docs/inbox-api/replies.md on the Rasket website.
1. Find every place this app calls POST /emails (or rasket.emails.send) for an email a customer might answer: receipts, order updates, account notices.
2. Add inbox: "support@yourdomain.com" to each of those sends. Remove reply_to from them: the two cannot be sent together.
3. Leave password resets and sign-in links alone; nobody should answer those.
4. Do not change anything else about the sends. Show me the diff.1. Add inbox to a send
Name a shared channel by its full address or its ID. Any key that may send can do this, because it routes mail in and reads nothing: no Inbox access is needed.
curl -X POST "https://api.rasket.com/emails" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: receipt-8412-october" \
-d '{
"from": "Lumen <billing@lumen.app>",
"to": ["priya@harborcoffee.co"],
"subject": "Your Lumen receipt — October",
"html": "<p>Thanks for your payment of $49.</p>",
"inbox": "support@lumen.app"
}'const response = await fetch("https://api.rasket.com/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
"Content-Type": "application/json",
"Idempotency-Key": "receipt-8412-october",
},
body: JSON.stringify({
from: "Lumen <billing@lumen.app>",
to: ["priya@harborcoffee.co"],
subject: "Your Lumen receipt — October",
html: "<p>Thanks for your payment of $49.</p>",
inbox: "support@lumen.app"
}),
});
const { id } = await response.json();import os
import requests
response = requests.post(
"https://api.rasket.com/emails",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
"Idempotency-Key": "receipt-8412-october",
},
json={
"from": "Lumen <billing@lumen.app>",
"to": ["priya@harborcoffee.co"],
"subject": "Your Lumen receipt — October",
"html": "<p>Thanks for your payment of $49.</p>",
"inbox": "support@lumen.app"
},
)
id = response.json()["id"]- The email goes out with
Reply-To: support@lumen.app. Don't sendreply_toas well. - It works the same on every email in a batch.
- A restricted channel needs a key limited to it. Personal mailboxes can't be named.
2. The customer replies
The reply opens a conversation in that channel, or joins the one already open for that email. Your team sees which email it answers. In the dashboard, the sent email shows Replies go to with the channel, and then Customer replied with a link to the conversation.
3. Find the conversation from your app
Read the email back. Once the customer has replied, it carries the conversation's ID; until then conversation_id is null.
curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"const response = await fetch("https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();import os
import requests
response = requests.get(
"https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]{
"inbox": {
"channel_id": "0192f6a8-3c1e-7a40-9b2d-5e8f1a6c4d20",
"conversation_id": "0192f8e1-7a40-7c55-b1e3-0f0a3b6d2e91"
}
}Or list the open conversations in the channel with a key that has Inbox access Read:
curl -X GET "https://api.rasket.com/inbox/conversations?status=open&limit=20" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"const response = await fetch("https://api.rasket.com/inbox/conversations?status=open&limit=20", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();import os
import requests
response = requests.get(
"https://api.rasket.com/inbox/conversations?status=open&limit=20",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())4. Hear about it
Turn on inbox.conversation.created and inbox.message.received on a webhook endpoint. Both name the conversation and carry no message text. They are on Events.
A reply counts as received mail, which is free. Your team's answer from the Inbox is a send, like any other email.
If something goes wrong
| Answer | What to do |
|---|---|
422 validation_error | inbox and reply_to were both sent. Keep inbox: it sets Reply-To for you. |
422 | The channel is unknown, your project has no Inbox yet, or the channel is restricted and the key is not limited to it. Write the full address, such as support@lumen.app — a bare support is ambiguous across your domains. |
| The reply did not arrive | Open the sent email in the dashboard. If it has no Replies go to line, the send did not name the channel; read it back with GET /emails/{email_id} to check. |