Intro
MailLane accepts allowlisted inbound email and hands accepted messages to Openclaw through pull mode or webhook mode.
If you are just getting started, use pull mode. It is the simplest setup and the recommended default. If you already run a public Openclaw hook endpoint, you can switch to webhook mode later.
If you are wiring this up with an agent runtime, jump to For agents for the canonical machine-readable setup manifests.
Choose your path
- Pull mode (recommended): Openclaw polls MailLane for pending accepted messages.
- Push mode / webhook mode (advanced): MailLane pushes accepted messages
into your Openclaw
/hooks/agentendpoint.
Prerequisites
- a MailLane account and a provisioned inbox
- the inbox id you want Openclaw to read from or receive pushes for
- a pull token for pull mode or an Openclaw hook token for push mode
- an allowlist that already matches the senders you expect
- for push mode, a public Openclaw URL that ends in
/hooks/agent
Important setup advice
If you already have a normal mailbox such as Gmail or Outlook, it is usually better to treat your MailLane address as a forwarding target from that main inbox instead of making MailLane the primary mailbox address for your bot. That keeps provider-specific mailbox workflows, login recovery, and human-facing email in your normal inbox while MailLane stays a narrow relay into Openclaw.
Before you enable forwarding:
- make sure Openclaw is already ready to process MailLane traffic
- confirm the senders you want to relay are on the inbox allowlist
- do not add a catch-all allowlist rule just to "make it work" first. A broad
rule such as
.*or an all-allowed entry removes MailLane's main security boundary and is strongly discouraged.
Some mailbox providers send a forwarding confirmation email before they begin relaying mail. Gmail does this. That confirmation message lands at MailLane first. If your bot is not explicitly configured to notice and handle that message, check the inbox's rejected email diagnostics list first so you can find the confirmation email and complete the provider's manual confirmation step.
Current functional limits
MailLane's current hosted defaults and customer-visible caps include:
- public routes currently use a 60-request / 60-second rate-limit window, so pull mode should poll on a modest interval instead of a tight loop
- pull mode keeps only the newest 10 pending accepted messages per inbox; when
older pending messages were dropped,
droppedCountreports how many andoverflowedbecomestrue - after a successful pull, MailLane keeps a 24-hour duplicate-suppression record for each consumed message, so provider retries during that window do not cause the same message to reappear as pending
- rejected email diagnostics keep only the newest 10 rejected emails per inbox when retention is enabled, and retention is on by default for new inboxes
- MailLane runs an antivirus and malware check on inbound mail. If MailLane detects a virus or malware, it rejects the message before allowlist acceptance or downstream delivery, and the rejected-email entry keeps metadata only instead of parsed body or thread content
Pull mode (recommended)
MailLane exposes pending accepted messages at GET /v1/inboxes/:inboxId/pending.
Send Authorization: Bearer <pull-token> with each request.
Each successful pull returns the current pending messages and hard-deletes the returned messages from MailLane.
After a successful pull, MailLane keeps a short 24-hour duplicate-suppression record for each consumed message. If your inbox provider retries the same email during that window, MailLane suppresses the duplicate instead of making that already-consumed message pending again.
Pull and push use the same accepted message object. Pull returns it directly in
messages[]. Push embeds the same JSON inside the Openclaw hook payload's
message field.
Pull example request
curl -sS \
-H "Authorization: Bearer <pull-token>" \
https://maillane.dev/v1/inboxes/<inbox-id>/pending
Pull example response
{
"droppedCount": 0,
"messages": [
{
"allowlistMatch": {
"ruleType": "exact_email",
"ruleValue": "alerts@example.com"
},
"id": "msg_123",
"message": {
"bodyText": "Build complete for deploy 123.",
"cc": [
{
"email": "ops@example.com",
"name": "Ops"
}
],
"from": {
"email": "alerts@example.com",
"name": "Build Bot"
},
"replyTo": [],
"sentAt": "2026-03-27T11:59:30.000Z",
"subject": "Build complete",
"thread": {
"inReplyTo": null,
"references": []
},
"to": [
{
"email": "agent@agent.maillane.dev",
"name": "Agent Inbox"
}
]
},
"occurredAt": "2026-03-27T12:00:00.000Z"
}
],
"overflowed": false
}
Pull response keys
droppedCount: how many older accepted messages MailLane dropped before this response because the inbox exceeded the pull retention capmessages: the accepted messages returned by this pulloverflowed:truewhen one or more older accepted messages were dropped
Accepted message keys
allowlistMatch: the allowlist rule that accepted the senderallowlistMatch.ruleType: the rule kind that matched, such asexact_emailallowlistMatch.ruleValue: the configured rule value that matchedid: MailLane's durable id for the accepted messagemessage: the normalized inbound email contentmessage.bodyText: the plain-text body extracted from the inbound emailmessage.cc: the normalized CC recipientsmessage.from: the normalized sendermessage.replyTo: the normalized Reply-To recipientsmessage.sentAt: when the upstream email says it was sentmessage.subject: the normalized subject linemessage.thread: threading metadatamessage.thread.inReplyTo: the upstream parent reference when present, ornullmessage.thread.references: the upstream reference chain in ordermessage.to: the normalized inbox recipientsoccurredAt: when MailLane accepted the message
Participant keys
email: the normalized email addressname: the optional display name when one was present in the inbound message
Minimum fields to process first
For a first working Openclaw flow, extract these fields from each pulled message:
idfor MailLane message trackingmessage.subjectandmessage.from.emailfor quick routing logicmessage.bodyTextfor the email content you want Openclaw to processoccurredAtfor ordering or freshness checksallowlistMatchif you want to branch on which allowlist rule accepted it
Push mode (webhook, advanced)
Use push mode only when your Openclaw instance already exposes a public
/hooks/agent endpoint and you want MailLane to push accepted messages
immediately.
- the target must end with
/hooks/agent - MailLane sends
Authorization: Bearer <openclaw-hook-token> - MailLane sends
Content-Type: application/json - secure HTTPS is the default expectation
- insecure HTTP is testing-only
- localhost and private-network targets are rejected
Use insecure HTTP only for temporary testing on systems you control. MailLane still rejects localhost and private-network webhook targets even in testing mode.
Webhook example payload
{
"deliver": false,
"message": "A new MailLane inbound email was accepted.\n\n{\"allowlistMatch\":{\"ruleType\":\"exact_email\",\"ruleValue\":\"alerts@example.com\"},\"id\":\"msg_123\",\"message\":{\"bodyText\":\"Build complete for deploy 123.\",\"cc\":[],\"from\":{\"email\":\"alerts@example.com\",\"name\":\"Build Bot\"},\"replyTo\":[],\"sentAt\":\"2026-03-27T11:59:30.000Z\",\"subject\":\"Build complete\",\"thread\":{\"inReplyTo\":null,\"references\":[]},\"to\":[{\"email\":\"agent@agent.maillane.dev\",\"name\":\"Agent Inbox\"}]},\"occurredAt\":\"2026-03-27T12:00:00.000Z\"}",
"name": "MailLane",
"wakeMode": "now"
}
The JSON after the blank line inside message is the same accepted message
object described in the pull section above.
Push payload keys
deliver: the Openclaw hook flag MailLane sets tofalsemessage: a short MailLane notice followed by a blank line and the accepted message JSONname: the sender label, alwaysMailLanewakeMode: the Openclaw wake hint, currentlynow
For agents
These setup manifests are intentionally machine-readable and can be consumed as
application/json.
Pull manifest
{
"mode": "pull",
"recommended_for": "beginner",
"maillane_endpoint": "GET /v1/inboxes/:inboxId/pending",
"http_method": "GET",
"auth_scheme": "Authorization: Bearer <pull-token>",
"required_fields": [
"inboxId",
"pullToken"
],
"request_example": {
"headers": {
"Authorization": "Bearer <pull-token>"
},
"method": "GET",
"url": "https://maillane.dev/v1/inboxes/<inbox-id>/pending"
},
"response_example": {
"droppedCount": 0,
"messages": [
{
"id": "msg_123",
"allowlistMatch": {
"ruleType": "exact_email",
"ruleValue": "alerts@example.com"
},
"message": {
"bodyText": "Build complete for deploy 123.",
"cc": [],
"from": {
"email": "alerts@example.com",
"name": "Build Bot"
},
"replyTo": [],
"sentAt": "2026-03-27T11:59:30.000Z",
"subject": "Build complete",
"thread": {
"inReplyTo": null,
"references": []
},
"to": [
{
"email": "agent@agent.maillane.dev",
"name": "Agent Inbox"
}
]
},
"occurredAt": "2026-03-27T12:00:00.000Z"
}
],
"overflowed": false
},
"verification_steps": [
"Call the pending endpoint with the inbox id and pull token.",
"Confirm the response includes a messages array.",
"Confirm successful pulls hard-delete returned accepted messages."
],
"constraints": [
"Polling must be enabled for the inbox.",
"Messages are removed from MailLane after a successful pull.",
"Public routes currently use a 60-request / 60-second rate-limit window.",
"After a successful pull, MailLane keeps a 24-hour duplicate-suppression record for consumed messages so provider retries in that window do not reappear as pending.",
"Only the newest 10 pending accepted messages are retained per inbox; older ones increment droppedCount and set overflowed=true."
]
}
Webhook manifest
{
"mode": "webhook",
"recommended_for": "advanced",
"maillane_endpoint": "POST <public-openclaw-url>/hooks/agent",
"target_path": "/hooks/agent",
"http_method": "POST",
"auth_scheme": "Authorization: Bearer <openclaw-hook-token>",
"content_type": "application/json",
"required_fields": [
"targetUrl",
"openclawHookToken"
],
"request_example": {
"headers": {
"Authorization": "Bearer <openclaw-hook-token>",
"Content-Type": "application/json"
},
"method": "POST",
"url": "https://openclaw.example.com/hooks/agent"
},
"payload_example": {
"deliver": false,
"message": "A new MailLane inbound email was accepted.\n\n{\"allowlistMatch\":{\"ruleType\":\"exact_email\",\"ruleValue\":\"alerts@example.com\"},\"id\":\"msg_123\",\"message\":{\"bodyText\":\"Build complete for deploy 123.\",\"cc\":[],\"from\":{\"email\":\"alerts@example.com\",\"name\":\"Build Bot\"},\"replyTo\":[],\"sentAt\":\"2026-03-27T11:59:30.000Z\",\"subject\":\"Build complete\",\"thread\":{\"inReplyTo\":null,\"references\":[]},\"to\":[{\"email\":\"agent@agent.maillane.dev\",\"name\":\"Agent Inbox\"}]},\"occurredAt\":\"2026-03-27T12:00:00.000Z\"}",
"name": "MailLane",
"wakeMode": "now"
},
"verification_steps": [
"Configure a public Openclaw hook target ending in /hooks/agent.",
"Store the bearer token in MailLane.",
"Send an allowlisted test message and confirm Openclaw receives the payload."
],
"constraints": [
"HTTPS is recommended.",
"Insecure HTTP is testing-only.",
"Localhost and private-network targets are rejected."
]
}
Verification
- Send one allowlisted email into the MailLane inbox.
- For pull mode, confirm Openclaw can fetch the pending message from
GET /v1/inboxes/:inboxId/pending. - For webhook mode, confirm MailLane accepts the public
/hooks/agenttarget and Openclaw receives the payload. - Confirm the payload shape matches your expected workflow input.
Troubleshooting
401 unauthorized: the pull token or webhook bearer token is missing or wrong429from the pending endpoint: back off and poll less aggressively; the current hosted default is a 60-request / 60-second rate-limit window409 polling_not_enabled: the inbox is not in pull mode- invalid webhook target: the target is not public or does not end with
/hooks/agent - no delivery: confirm the sender matches the inbox allowlist before changing anything else
- forwarding never starts from Gmail or another mailbox provider: check the rejected email diagnostics list for a forwarding-confirmation email that still needs manual approval