Personalized Mail with unique postcard PDFs

Use POST /v1/postcards/mailings to prepare 1–500 individually designed postcards as one campaign. For 125–500 eligible promotional postcards sent to saved Canadian recipients without PHI, choose mail_class: "standard", content_category: "promotional" and attestation_accepted: true. Each item can have different front and back PDFs. They do not need separate templates or separate orders. Postally's operating minimum for Personalized Mail is 125 pieces; quantity alone does not establish postal eligibility. The pieces must share a qualifying promotional purpose and meet the applicable design requirements.

Repeated POST /v1/postcards/create calls price each request as one piece. Adding the same bundle_key does not combine those calls for Personalized Mail eligibility. The legacy POST /v1/postcards/batch requires template IDs and does not accept per-item PDF URLs. Use the mailing workflow below for unique PDFs in one campaign.

1. Choose the supported workflow

The mailing must use one shared postcard format, paper finish and postal layout. Supported formats are postcard_4x6, postcard_5x7, postcard_6x9 and postcard_6x11. Only saved Canadian recipients without PHI are supported by this workflow. Recipient IDs must belong to your account and remain mailable through submission. The same recipient may appear more than once, but each item reference must be unique.

For 1–124 pieces or non-promotional content, use mail_class: "first_class" (Lettermail). When mail_class is omitted it is auto-selected exactly as on the other create endpoints: Lettermail below 125 pieces or for transactional/general content, Personalized Mail (standard, which still requires attestation_accepted: true) for 125 or more promotional pieces. The response echoes the resolved mail_class. An explicit standard that does not qualify is refused with 400 mail_class_policy_violation and suggested_mail_class: "first_class"; it is never silently downgraded. content_category is always required and must truthfully be promotional, transactional or general. Do not label transactional mail promotional to obtain the lower rate.

For more than 500 items, create separate mailings of at most 500 items each. Every mailing using standard must independently contain at least 125 eligible promotional items. For example, 600 items can be two 300-item mailings; a 500/100 split would require Lettermail for the 100-item mailing. Separate mailings have separate quotes, submissions and campaigns. Mixed formats, non-Canadian destinations and PHI need another supported workflow or guidance from support; this endpoint cannot accept them.

2. Prepare recipients, artwork and access

Verify your account email before creating, retrying or submitting a mailing. Use an API key in the Authorization: Bearer YOUR_API_KEY header. Keep credentials in your application or secret store, never in artwork URLs, prompts or support emails. API base URL: https://api.postally.ca.

Scoped API keys need postcards.write for create, retry, submit and pre-submission cancellation. Polling uses postcards.read (also allowed by postcards.write). Prepared PDF previews need mailpieces.read. If you create recipients through POST /v1/recipients/new, also grant recipients.write. Cancelling a submitted campaign needs campaigns.write. Keys and any sessions derived from them retain their permissions and test/live restrictions.

Supply a publicly reachable HTTP(S) URL for each single-page front PDF and optionally each single-page back PDF. Use PDF spec_version: 2 with full bleed. A 4×6 postcard PDF measures 6.25×4.25 inches (450×306 points), not the final trim size. Follow the selected format and layout's design guide and keep postal zones clear. Public design guides are available from https://api.postally.ca/docs. Account sessions with owner access can also inspect current dimensions and supported layouts at GET /v1/specs. Postally adds postal elements; do not bake conflicting delivery addresses or postage into the artwork.

front_pdf_url is the address side: Postally prints the delivery address, the return address and the postage mark on it, so design that side around the address area in the design guide. The back is printed exactly as supplied; omit back_pdf_url for a blank back. Each file must be one landscape page sized for the selected format with full bleed, within 3 points of the guide's dimensions, with no crop marks, slug or printer marks. Draw the page landscape rather than relying on a page rotation setting: Postally places the postal elements on the unrotated page, so a rotated page can print them sideways. Each file must be at most 50 MB, must download within 15 seconds, and must be at least 5 KB; the front must carry real drawn content on its page (text, drawn shapes, an image or shading): a front with content is accepted at any file size, a front that draws nothing is refused when its file is under 20 KB, and a larger front that draws nothing is currently accepted and recorded for review rather than refused — do not count on a blank front being caught. Password-protected or permission-restricted PDFs cannot be read and are refused. Postally does not check or convert colour; files print as supplied, so export them in the colour space you want printed.

Artwork URLs must remain accessible while preparation runs. URLs are subject to public-network safety checks; private/local network hosts are not supported. Source URLs are not returned in mailing status responses. A retry can reuse artwork already stored, so changing a file at the same URL is not a supported way to revise a mailing. Signed (expiring) URLs are fine if they stay valid for 24 hours after you create the mailing, the window in which preparation and retries can download them. Alternatively, host each PDF with Postally: request an upload URL with POST /v1/uploads/presign (JSON body with filename ending in .pdf, content_type: "application/pdf", purpose: "template" and the file's exact file_size in bytes, up to 20 MB per file). Then upload the PDF with an HTTP PUT to the returned upload_url, sending the file bytes with Content-Type: application/pdf, and use the returned file_url as front_pdf_url or back_pdf_url. The presign request alone uploads nothing.

3. Create a test mailing

The following JavaScript builds a complete test manifest from your 125–500 records. Replace each record's recipientId, frontPdfUrl and optional backPdfUrl with your own saved recipient UUID and hosted PDF URLs. Use opaque references without names, addresses or other personal information.

if (records.length < 125 || records.length > 500) {
  throw new Error('Use 125–500 records for this Personalized Mail example.');
}
const manifest = {
  name: 'Unique postcard campaign',
  format: 'postcard_4x6',
  mail_class: 'standard',
  content_category: 'promotional',
  attestation_accepted: true,
  test: true,
  spec_version: 2,
  address_layout: 'panel',
  paper_finish: 'glossy',
  return_address_mode: 'full',
  postal_zone_background: 'white',
  back_barcode_background: 'white',
  items: records.map((record, index) => ({
    reference: `card-${index + 1}`,
    recipient_id: record.recipientId,
    front_pdf_url: record.frontPdfUrl,
    ...(record.backPdfUrl ? { back_pdf_url: record.backPdfUrl } : {}),
  })),
};

For 1–124 cards, or content that is not promotional, use Lettermail. A first_class mailing needs no attestation (omitting mail_class selects it automatically for this manifest):

if (records.length < 1 || records.length > 124) {
  throw new Error('Use 1–124 records for this Lettermail example.');
}
const manifest = {
  name: 'Unique postcard Lettermail mailing',
  format: 'postcard_4x6',
  mail_class: 'first_class',
  content_category: 'general',
  test: true,
  spec_version: 2,
  address_layout: 'panel',
  items: records.map((record, index) => ({
    reference: `card-${index + 1}`,
    recipient_id: record.recipientId,
    front_pdf_url: record.frontPdfUrl,
    ...(record.backPdfUrl ? { back_pdf_url: record.backPdfUrl } : {}),
  })),
};

Send either manifest as JSON using POST /v1/postcards/mailings with Content-Type: application/json, your Authorization header and a required Idempotency-Key header. Generate and persist one key for this logical mailing before the first request. Reuse the same key and normalized manifest after a network timeout; do not create a second mailing to retry an uncertain create. The key must be 1–255 printable non-space ASCII characters. A changed manifest with the same key returns a conflict. A successful create returns HTTP 202 with the mailing id and preparation status.

Use format, not size. The mailing name is required and at most 255 characters. Each reference is 1–100 characters, starts with a letter or digit, and then uses only letters, digits, dots, underscores, colons or hyphens. References are case-sensitive and unique within the mailing. Each item accepts only reference, recipient_id, front_pdf_url and optional back_pdf_url. Inline recipients, template_id, merge_data, QR add-ons, scheduling options and arbitrary metadata are not supported; unknown fields return a validation error.

A test key forces test mode even if test: false is supplied. A test mailing cannot be converted to live. Test submission does not debit credits, print or mail anything, and emits no production webhooks. After testing, create a separate live mailing with a live key, a new Idempotency-Key and test: false; review its prepared PDFs and current quote again before submitting it.

4. Poll, inspect every PDF and review the quote

Poll GET /v1/postcards/mailings/:id with your Authorization header. Use bounded polling with backoff and respect rate-limit responses. There is no mailing-ready webhook. Status values are preparing, ready, blocked, submitted, cancelled and expired. If preparation remains incomplete, inspect status and item errors; do not automatically submit or create duplicates.

The response includes total, ready, failed, item status and each ready item's authenticated preview_url. Resolve each relative preview URL against https://api.postally.ca and retrieve it with an appropriately scoped API key. Review every distinct prepared PDF, including its postal elements. This workflow uses prepared-PDF review followed by submission, not template-campaign proof approval. Do not call template campaign approval or retry-dispatch to advance a mailing.

When ready, review the returned quote, including subtotal_cents, tax_cents, campaign_fee_cents, total_cents, token and expires_at. Amounts are in CAD cents. Lettermail (first_class) mailings never carry a small-order fee. For Personalized Mail (standard), any configured small-order fee is included in the quote as campaign_fee_cents; rates come from the current quote, so do not infer an exact charge from quantity. Preparation and the quote expire 24 hours after creation, including time spent preparing. Retrying does not extend that deadline.

One invalid item blocks the entire mailing. A mailing that is blocked, incomplete, cancelled or expired cannot be submitted. Recipient or account changes can invalidate the reviewed result; obtain the current status and follow its error before proceeding. If changed data or expiry requires a replacement, create and review a new mailing rather than silently modifying the old one.

5. Submit the ready mailing once

Live mailings use prepaid credits. Creating and reviewing a mailing does not charge credits or release it for printing. Submit only after all items are ready, you have reviewed the PDFs and current total, and the live account has enough credits for the full quote. This workflow does not create a Stripe Checkout payment link.

Call POST /v1/postcards/mailings/:id/submit with your Authorization and JSON Content-Type headers. Copy the current quote.token from the status response into this body:

{"quote_token":"COPY_CURRENT_QUOTE_TOKEN_HERE"}

Submission atomically creates one campaign and links and releases the already prepared mailpieces; for a live mailing, it debits the quoted total once. Insufficient credits or an invalid quote blocks the entire submission. If the response is lost, poll the same mailing ID and retry its submit action as needed; a successful submission replay returns the existing campaign without another debit. Do not resend those items using single-send or batch endpoints. Poll status for the resulting campaign_id.

After submission, do not edit, delete or suppress any recipient in the mailing until it is printed. A submitted mailing prints only as one complete set: a change to any recipient's name or address, deleting it or suppressing it holds the whole mailing out of print, and a changed recipient cannot be restored into the prepared mailing. Saving a recipient again with exactly the same values is not a change and does not hold it. When a mailing is held, Postally emails the account owner once, and GET /v1/postcards/mailings/:id shows print_hold with the time, a reason code and the affected item references while status stays submitted; nothing more is charged, and nothing is cancelled or refunded automatically. To get the credits back, cancel the campaign as described in section 6, then create a new mailing with the corrected recipients, or contact support with the mailing ID.

Live submitted pieces proceed through production; mailpiece.created events are queued, but no campaign.approved event is emitted by this workflow. Test pieces remain at created after successful submission and do not simulate production or delivery. Neither Lettermail nor Personalized Mail provides per-piece carrier tracking or delivery confirmation.

6. Retry failures or cancel the whole mailing

For retryable preparation failures, use POST /v1/postcards/mailings/:id/retry with JSON {} and then poll again. Retry only reprocesses failed items; it does not edit the immutable manifest, change artwork URLs, extend expiry or turn test mode off. If corrected URLs or other manifest changes are needed, create a new mailing with a new Idempotency-Key. Some persisted preparation failures require a new mailing; follow new_mailing_required rather than trying to edit the original.

Before submission, cancel with POST /v1/postcards/mailings/:id/cancel and JSON {}. No credits have been charged. After submission, use the returned campaign ID in the following request to POST /v1/campaigns/cancel:

{"id":"COPY_SUBMITTED_CAMPAIGN_UUID_HERE"}

Submitted artwork mailings must be cancelled as a whole campaign. Individual mailpiece cancellation is refused with artwork_mailing_managed. Cancellation must be before production begins: normally the nightly print run after the end of the submission day in America/Vancouver. A mailing that is held out of print (print_hold in its status, section 5) never enters production, so it stays cancellable this way until you cancel it; cancelling is the only way to recover its credits. Eligible live whole-campaign cancellation refunds its piece charges and any charged small-order fee atomically to prepaid credits. This fee-refund rule is specific to this artwork-mailing workflow; do not apply it to ordinary prepaid campaigns or legacy API auto-bundles. Never promise a refund or cancellation after production has started.

Troubleshooting and authoritative references

For a validation error, check the exact fields and eligibility rather than sending the items one at a time to force Personalized Mail. For email_not_verified, verify the account email. For insufficient_permissions, use a key with the required scopes. For insufficient credits, fund the account and re-check the unexpired mailing before retrying submission. For an uncertain submit response, inspect the same mailing before attempting any replacement. For terminal errors, unexplained debit/refund discrepancies or unsupported requirements, contact support with the mailing/campaign ID and sanitized error code; never email API keys, recipient addresses or signed artwork URLs.

Customer guide: https://postally.ca/guides/personalized-mail-api. Plain-text Markdown of this same guide: https://api.postally.ca/v1/docs/postcard-mailings. Full API reference, section 11.5.1: https://api.postally.ca/v1/docs/download/api-reference. Current public rates: https://postally.ca/pricing. Technical help: support@postally.ca. Sales questions: sales@postally.ca.