Create a self-mailer (BILLABLE)
lob_self_mailers_createCommit a self-mailer send by providing inside and outside content, recipient, and sender addresses. Requires a confirmation token from preview in live mode.
Instructions
Commit a self-mailer send. Billable in live mode. Requires a confirmation_token from lob_self_mailers_preview that matches the current payload (live mode only). Sizes: 6x18_bifold (default), 11x9_bifold.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| description | No | Internal description (max 255 chars). | |
| to | Yes | Recipient address. Either a saved address ID (`adr_…`) or an inline address. | |
| from | Yes | Sender (return) address. Either a saved address ID (`adr_…`) or an inline address. | |
| send_date | No | ISO 8601 timestamp (e.g. '2026-05-01T00:00:00Z') to schedule the send. Must be at most 180 days in the future. | |
| mail_type | No | Mail class. Defaults to usps_first_class for most pieces. | |
| merge_variables | No | Key/value pairs substituted into Handlebars-style {{variables}} in your HTML/template content. | |
| metadata | No | Up to 20 string key/value pairs to attach to the resource. | |
| billing_group_id | No | Billing group ID (`bg_…`) to attribute the charge to. | |
| use_type | No | Required for some mail classes. 'marketing' for promotional, 'operational' for transactional. | |
| inside | Yes | Inside-of-self-mailer content source. | |
| outside | Yes | Outside-of-self-mailer content source. | |
| size | No | Self-mailer size. Defaults to 6x18_bifold. | |
| idempotency_key | No | Idempotency key (max 256 chars). If omitted, the server auto-generates a value derived from the confirmation_token when present, otherwise a fresh UUIDv4. Lob deduplicates identical keys for 24 hours. | |
| extra | No | Additional Lob API parameters not enumerated above. Merged into the request body verbatim. See https://docs.lob.com for the full parameter list per resource. | |
| confirmation_token | No | Token from lob_self_mailers_preview. Required in live mode (LOB_LIVE_MODE=true). |
Implementation Reference
- src/tools/self-mailers.ts:107-119 (handler)The handler for lob_self_mailers_create is `pc.commit` — the commit function returned by buildPreviewCommit. When invoked, it validates the confirmation_token (required in live mode), checks payload hash matches the preview, derives idempotency key, calls beforeDispatch (piece-counter), and finally makes a POST to /self_mailers via callCommit.
registerTool(server, { name: "lob_self_mailers_create", annotations: { title: "Create a self-mailer (BILLABLE)", ...ToolAnnotationPresets.commit, }, description: "Commit a self-mailer send. **Billable** in live mode. Requires a `confirmation_token` from " + "lob_self_mailers_preview that matches the current payload (live mode only). " + "Sizes: 6x18_bifold (default), 11x9_bifold.", inputSchema: selfMailerCommitShape, handler: pc.commit, }); - src/preview/preview-commit.ts:80-92 (handler)The callCommit closure passed by registerSelfMailerTools — makes the actual Lob API POST to /self_mailers with the stripped payload and idempotency key. This is what eventually sends the self-mailer.
expiresAt: now + ttlMs, }; ctx.tokenStore.put(record); return { confirmation_token: token, expires_at: new Date(record.expiresAt).toISOString(), preview: previewResponse, }; }, async commit(input, serverCtx) { const inputAny = input as Record<string, unknown>; const confirmationToken = inputAny.confirmation_token as string | undefined; - src/tools/self-mailers.ts:31-50 (schema)Input schema for the tool. selfMailerCreateShape defines the base fields (inside, outside, size, to, from, etc.) and selfMailerCommitShape extends it with an optional confirmation_token.
const selfMailerCreateShape = { ...mailPieceCommonShape, inside: contentSourceSchema.describe("Inside-of-self-mailer content source."), outside: contentSourceSchema.describe("Outside-of-self-mailer content source."), size: SELF_MAILER_SIZE.optional().describe( "Self-mailer size. Defaults to 6x18_bifold.", ), idempotency_key: idempotencyKeyAutoSchema, extra: extraParamsSchema, } as const; const selfMailerCommitShape = { ...selfMailerCreateShape, confirmation_token: z .string() .optional() .describe( "Token from lob_self_mailers_preview. Required in live mode (LOB_LIVE_MODE=true).", ), } as const; - src/tools/register.ts:28-38 (registration)The top-level registration entry point. registerAllTools calls registerSelfMailerTools(server, lob, tokenStore, pieceCounter) which registers lob_self_mailers_create along with the other self-mailer tools.
export function registerAllTools( server: McpServer, lob: LobClient, tokenStore: TokenStore, pieceCounter: PieceCounter, ): void { registerAddressBookTools(server, lob); registerVerificationTools(server, lob); registerPostcardTools(server, lob, tokenStore, pieceCounter); registerLetterTools(server, lob, tokenStore, pieceCounter); registerSelfMailerTools(server, lob, tokenStore, pieceCounter); - src/tools/helpers.ts:85-117 (registration)The registerTool utility that registers the tool with the MCP server, wrapping the handler with error handling and JSON serialization.
export function registerTool<TShape extends ZodRawShape>( server: McpServer, def: ToolDefinition<TShape>, ): void { const a = def.annotations ?? {}; server.registerTool( def.name, { title: a.title ?? def.name, description: def.description, inputSchema: def.inputSchema, annotations: { ...a, // Lob is always external; default the hint accordingly. openWorldHint: a.openWorldHint ?? true, }, }, // The SDK's ToolCallback type is parameterised over the exact ZodRawShape and // resists the generic erasure here. The runtime contract (validated args in, // CallToolResult out) is correct, so we bridge the type boundary with `as never`. (async (args: unknown, serverCtx: unknown): Promise<CallToolResult> => { try { const result = await def.handler(args as never, serverCtx); return { content: [{ type: "text", text: stringifyResult(result) }] }; } catch (err) { return { isError: true, content: [{ type: "text", text: formatErrorForTool(err) }], }; } }) as never, ); }