{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/node-sdk-receiving-email","markdown_url":"https://test.abhinandan.one/node-sdk-receiving-email.md","article":{"id":"0afad457-dd33-4882-91ba-3b85d087dccf","article_slug":"node-sdk-receiving-email","parent_article_slug":null,"parent_article_title":null,"kind":"guide","published_at":"2026-08-11T18:55:07.831644+00:00","keywords":["primitive.receive","ReceivedEmail","@primitivedotdev/sdk/webhook","normalizeReceivedEmail","email.raw","receive inbound webhook"],"meta_description":"primitive.receive() verifies the webhook signature and returns a normalized ReceivedEmail with sender, replyTarget, thread, and raw fields.","og_image_url":null,"source_file_paths":["sdk-node/README.md","sdk-node/src/webhook/index.ts"],"recording_id":null,"replayable":false,"task_name":"Receiving Inbound Email","category":"Node.js SDK","summary":null,"description":"Turn a raw inbound webhook delivery into a normalized ReceivedEmail object with primitive.receive, ready to pass straight into client.reply or client.forward.","content_kind":"repo_page","content_markdown":"`primitive.receive(...)`, the root export of `@primitivedotdev/sdk`, verifies an inbound webhook delivery and returns a `ReceivedEmail`: a normalized object with a stable shape you can act on immediately, instead of the raw [`email.received` event](webhook-events). Call it at the top of any handler that receives inbound mail, whether that's a Next.js route, an Express endpoint, or a Primitive Function.\n\n## Receive from a standard `Request`\n\nPass the `Request` straight to `primitive.receive` when your framework hands you a Fetch API `Request` object, as Next.js App Router and Cloudflare Workers do. This overload is async.\n\n<Steps>\n\n<Step title=\"Install the SDK and set your webhook secret\">\n\n```bash\nnpm install @primitivedotdev/sdk\nexport PRIMITIVE_WEBHOOK_SECRET=whsec_...\n```\n\n</Step>\n\n<Step title=\"Call primitive.receive with the request and your secret\">\n\n```ts\nimport primitive from \"@primitivedotdev/sdk\";\n\nexport const runtime = \"nodejs\";\nexport const maxDuration = 300;\n\nconst client = primitive.client({\n  apiKey: process.env.PRIMITIVE_API_KEY!,\n});\n\nexport async function POST(req: Request) {\n  const email = await primitive.receive(req, {\n    secret: process.env.PRIMITIVE_WEBHOOK_SECRET!,\n  });\n\n  await client.reply(email, \"Thank you for your email.\");\n\n  return Response.json({ ok: true });\n}\n```\n\n</Step>\n\n<Step title=\"Verify the result\">\n\n`primitive.receive(...)` reads the request body, verifies the HMAC-SHA256 signature against your account secret, and resolves to a `ReceivedEmail`. It rejects a delivery whose timestamp is more than 300 seconds off your clock, and a tampered or expired delivery throws instead of returning. See [Node.js SDK Errors](node-sdk-errors) for the error types and what triggers each one.\n\n</Step>\n\n</Steps>\n\n## Receive from a raw body and headers\n\nPass `{ body, headers, secret }` when your framework doesn't expose a standard `Request` object, for example a plain Node HTTP handler or Express with `express.raw({ type: \"application/json\" })`.\n\n```ts\nimport primitive from \"@primitivedotdev/sdk\";\n\nconst email = primitive.receive({\n  body: req.body, // string or Buffer, exact bytes as received\n  headers: req.headers,\n  secret: process.env.PRIMITIVE_WEBHOOK_SECRET!,\n});\n```\n\nThis overload is synchronous and returns a `ReceivedEmail` directly, not a `Promise`. `body` must be the exact bytes of the HTTP request before any JSON parsing; a body that has been parsed and re-serialized will fail signature verification on insignificant whitespace alone.\n\n<Tip>\n\nOnly need to verify a signature without normalizing the payload? Use `verifyWebhookSignature` from `@primitivedotdev/sdk/webhook` directly. See [Webhook Signature Verification](node-sdk-webhook-signing).\n\n</Tip>\n\n## The ReceivedEmail shape\n\n`ReceivedEmail` is the flat, normalized shape `primitive.receive(...)` returns, with the sender, recipient, subject, body, and threading fields promoted to the top level:\n\n```ts\nemail.sender.address;\nemail.sender.name;\n\nemail.receivedBy;\nemail.receivedByAll;\n\nemail.replyTarget.address;\nemail.replySubject;\nemail.forwardSubject;\n\nemail.subject;\nemail.text;\n\nemail.thread.messageId;\nemail.thread.references;\n\nemail.raw;\n```\n\n| Field | Description |\n| --- | --- |\n| `sender.address`, `sender.name` | The From address, parsed leniently for display. Falls back to the SMTP envelope sender when the header can't be parsed, so it is not a safe authorization anchor. See [Verifying Inbound Email Authenticity](node-sdk-email-authenticity). |\n| `receivedBy` | The recipient address this email was received on. |\n| `receivedByAll` | Every recipient address on the delivery. |\n| `replyTarget.address` | The Reply-To address when the inbound email carried one, otherwise the sender. Fully sender-controlled, so never authorize on it. |\n| `replySubject` | The `Re: <parent>` subject `client.reply` uses. |\n| `forwardSubject` | The `Fwd: <parent>` subject `client.forward` uses. |\n| `subject` | The original inbound subject line. |\n| `text` | The plain-text body, when present. |\n| `thread.messageId`, `thread.references` | Threading headers used to derive `In-Reply-To` and `References` on replies. |\n| `raw` | The full, schema-validated [`email.received` event](webhook-events) this object was normalized from. |\n\nUse `email.raw` whenever you need something outside the normalized shape: the original headers, SPF/DKIM/DMARC results, attachment metadata, or the raw MIME download URL.\n\n<Note>\n\nHand `email` straight to `client.reply(email, ...)` or `client.forward(email, ...)`; see [Sending, Replying, and Forwarding Email](node-sdk-sending-email) for both. Recipients, subject, and threading headers on a reply are derived server-side from the inbound row the email's id points to, not recomputed client-side.\n\n</Note>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Sending, Replying, and Forwarding Email\" href=\"node-sdk-sending-email\">\n\nReply to or forward the ReceivedEmail you just normalized.\n\n</Card>\n\n<Card title=\"Verifying Inbound Email Authenticity\" href=\"node-sdk-email-authenticity\">\n\nDecide whether to trust the sender before acting on the email.\n\n</Card>\n\n<Card title=\"Webhook Signature Verification\" href=\"node-sdk-webhook-signing\">\n\nVerify the Primitive-Signature header manually when you don't have a standard Request.\n\n</Card>\n\n<Card title=\"Node.js SDK Errors\" href=\"node-sdk-errors\">\n\nLook up WebhookVerificationError, WebhookPayloadError, and WebhookValidationError codes.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-node/README.md","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Receiving+Inbound+Email&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fnode-sdk-receiving-email","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}