{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/node-sdk-contract-module","markdown_url":"https://test.abhinandan.one/node-sdk-contract-module.md","article":{"id":"0bf15850-5cce-4b97-bd46-94d9aa119923","article_slug":"node-sdk-contract-module","parent_article_slug":null,"parent_article_title":null,"kind":"guide","published_at":"2026-08-11T18:54:59.877929+00:00","keywords":["buildEmailReceivedEvent","buildEventFromParsedData","@primitivedotdev/sdk/contract","EmailReceivedEventInput","email.received fixture","ParsedInput"],"meta_description":"buildEmailReceivedEvent and buildEventFromParsedData construct schema-valid email.received webhook payloads from @primitivedotdev/sdk/contract.","og_image_url":null,"source_file_paths":[],"recording_id":null,"replayable":false,"task_name":"Building Webhook Payloads (Contract Module)","category":"Node.js SDK","summary":null,"description":"Construct schema-valid email.received webhook payloads with the Node SDK's contract module, for anyone producing test fixtures or simulating inbound mail that must match Primitive's wire contract exactly.","content_kind":"repo_page","content_markdown":"The `contract` subpath of `@primitivedotdev/sdk` (`@primitivedotdev/sdk/contract`) builds schema-valid `email.received` webhook payloads in code, instead of you hand-assembling JSON that has to match Primitive's wire contract byte-for-byte. Reach for it when you're writing test fixtures or a mock inbound-webhook producer that must emit a payload the SDKs' `receive()` / `handleWebhook()` will accept.\n\nThis module is Node-only, separate from the Workers-safe `@primitivedotdev/sdk/api` subpath used inside Primitive Functions handlers. Import it from tooling and test code, not from a deployed Function.\n\n<Note>\n\nIf you're consuming inbound webhooks rather than producing them, you don't need this page. Use `primitive.receive(...)` as described in [Receiving Inbound Email](node-sdk-receiving-email), and see the [Inbound and Outbound Email Model](email-model) for the normalized `ReceivedEmail` shape and the raw `email.received` event this module builds.\n\n</Note>\n\n## What the contract module builds\n\nThe contract module assembles the full `EmailReceivedEvent` envelope from the pieces of an email, which is the inverse of parsing one. That envelope is the shape validated by the shared JSON Schema at `json-schema/email-received-event.schema.json` and catalogued on [Webhook Events Overview](webhook-events).\n\nTwo entry points cover the common cases:\n\n| Function | Use when |\n| --- | --- |\n| `buildEmailReceivedEvent` | You're writing the input by hand for a specific test case and want the full event envelope built for you. |\n| `buildEventFromParsedData` | You already have an intermediate `ParsedInput` shape, for example the output of your own MIME-parsing step, and want that folded into the envelope. |\n\nBoth live under `@primitivedotdev/sdk/contract`, alongside the producer-side TypeScript types documented on [Primitive Contract Types Reference](node-sdk-contract-types) (`EmailReceivedEventInput`, `ParsedInput`, `RawContentInline`, `RawContentDownloadOnly`).\n\n## Build a fixture from a hand-written input\n\nInstall the SDK, import `buildEmailReceivedEvent` with its input type, and hand it an `EmailReceivedEventInput`. The result is a complete `EmailReceivedEvent` you can serialize as a webhook body.\n\n<Steps>\n\n<Step title=\"Install the SDK\">\n\nThe contract module ships inside the main package; there's no separate install.\n\n```bash\nnpm install @primitivedotdev/sdk\n```\n\n</Step>\n\n<Step title=\"Import the builder and its input type\">\n\n```typescript\n// fixtures/inbound.ts\nimport { buildEmailReceivedEvent } from \"@primitivedotdev/sdk/contract\";\nimport type { EmailReceivedEventInput } from \"@primitivedotdev/sdk/contract\";\n```\n\nThe exact field list on `EmailReceivedEventInput` is on [Primitive Contract Types Reference](node-sdk-contract-types); build your input against those types so the compiler tells you what's missing.\n\n</Step>\n\n<Step title=\"Build the event\">\n\n```typescript\n// fixtures/inbound.ts (continued)\nconst input: EmailReceivedEventInput = {\n  /* fields per the contract types reference */\n} as EmailReceivedEventInput;\n\nconst event = buildEmailReceivedEvent(input);\n\nconsole.log(event.event); // \"email.received\"\n```\n\nThe result is a full `EmailReceivedEvent` object: the same shape `primitive.receive(...)` normalizes and `validateEmailReceivedEvent` accepts.\n\n</Step>\n\n<Step title=\"Serialize it as a webhook body\">\n\n```typescript\n// fixtures/inbound.ts (continued)\nconst body = JSON.stringify(event);\n// POST `body` to your handler, or pass it straight into\n// validateEmailReceivedEvent(JSON.parse(body)) to confirm it's schema-valid.\n```\n\n</Step>\n\n</Steps>\n\n## Build from an intermediate ParsedInput shape\n\nUse `buildEventFromParsedData` when your harness already has its own MIME-parsing step whose output is close to, but not exactly, the wire shape, so you don't hand-map every field.\n\n```typescript\n// fixtures/from-parsed.ts\nimport { buildEventFromParsedData } from \"@primitivedotdev/sdk/contract\";\nimport type { ParsedInput } from \"@primitivedotdev/sdk/contract\";\n\nconst parsed: ParsedInput = {\n  /* your parser's output, mapped to ParsedInput */\n} as ParsedInput;\n\nconst event = buildEventFromParsedData(parsed);\n```\n\n`buildEventFromParsedData` is the right call when you're bridging from another parser's output; `buildEmailReceivedEvent` is the right call when you're writing the input by hand.\n\n<Tip>\n\nRaw content is a discriminated union: use `RawContentInline` when the raw bytes are included inline, and `RawContentDownloadOnly` when you're modelling content too large to inline. The schema discriminates on `included`, so a partial mix of the two shapes is rejected. Full field lists are on [Primitive Contract Types Reference](node-sdk-contract-types).\n\n</Tip>\n\n## Validate what you built\n\nRun every built event through `validateEmailReceivedEvent` from `@primitivedotdev/sdk/webhook`, the same validator your handler uses in production, before you rely on the fixture.\n\n```typescript\n// fixtures/validate.ts\nimport { validateEmailReceivedEvent } from \"@primitivedotdev/sdk/webhook\";\n\nconst validated = validateEmailReceivedEvent(event);\n```\n\n`validateEmailReceivedEvent` throws a `WebhookValidationError` when the payload fails the schema; `safeValidateEmailReceivedEvent` from the same subpath returns a result instead of throwing.\n\n<Warning>\n\nKeep this validation step in CI. A future schema change that tightens a constraint should fail your fixture build, not a customer's production handler.\n\n</Warning>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Primitive Contract Types Reference\" href=\"node-sdk-contract-types\">\n\nBrowse every producer-side type (EmailReceivedEventInput, ParsedInput, RawContentInline/DownloadOnly) exported from the contract module.\n\n</Card>\n\n<Card title=\"Receiving Inbound Email\" href=\"node-sdk-receiving-email\">\n\nSee how primitive.receive() normalizes the email.received event this module builds.\n\n</Card>\n\n<Card title=\"Webhook Events Overview\" href=\"webhook-events\">\n\nUnderstand the shared webhook contract, signature verification, and the full event catalog this payload shape belongs to.\n\n</Card>\n\n<Card title=\"Parsing Raw Email (.eml)\" href=\"node-sdk-parsing-email\">\n\nParse real MIME bytes into structured bodies and attachments, the inverse operation of building a fixture by hand.\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":null,"raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Building+Webhook+Payloads+%28Contract+Module%29&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fnode-sdk-contract-module","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}