Real post, from one API call.
Send a bespoke printed card, an A5 postcard flyer or a letter to anyone in the UK — designed (even from their own website), printed and posted for you. Then get every scan, click, lead and sale back as a webhook. Works with any CRM, shop, app or no-code tool.
Quick start
1. In the Studio, save a campaign (your design and message, or "Bespoke for every company"). Its number is your template_id.
2. Copy your API key from API & integrations.
3. Send:
curl https://www.personalizedmarketing.co.uk/api/v1/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_id": 123,
"recipient": {
"name": "Sarah Jones",
"company": "Brightside Dental",
"address1": "12 High Street",
"city": "Guildford",
"postcode": "GU1 3AA",
"website": "https://brightsidedental.co.uk",
"email": "sarah@brightsidedental.co.uk"
},
"reference": "crm-contact-8812"
}' {
"ok": true,
"id": 4512,
"status": "designing",
"format": "card",
"bespoke": true,
"offer_code": "K7M2QX",
"page": "https://www.personalizedmarketing.co.uk/b/?t=9fk2ma7q",
"credits_used": 1
} That's it — it is designed, printed and posted. Printing and UK postage are included in every credit.
Authentication
Send your key on every request as Authorization: Bearer YOUR_API_KEY. Keep it secret — anyone with it can send post on your account. Make a new key any time in the Studio (old ones stop working at once). Base URL: https://www.personalizedmarketing.co.uk/api/v1
POST/send
Queue one item — or up to 500 with recipients. Each uses one credit (plus any gift); your balance is checked first.
| Field | Type | What it does |
|---|---|---|
template_id | int | A saved campaign in your Studio — its design, message, offer, A/B test, gift, follow-up and booking link are all used. |
automation_id | int | Or: an automation (uses its campaign). |
design | object | Or: a design right in the request — see Designs. |
type | string | letter (A4, in an envelope), flyer (A5 postcard, both sides), card (folded, in an envelope) or digital (no post — a personalised image and personal page for your own emails; only name and ideally email are needed). Defaults to the campaign's. |
email_it | bool | Digital only: true and we email each person their design from your company name (replies come to you). Uses 2 digital credits per person. |
recipient | object | name*, address1*, address2, city*, postcode*, company, website (for bespoke designs), email (for the follow-up), and any of your own fields — use them in your message as {field_name}. Shopify and HubSpot shapes are understood as they are.No address? Send just name and email: we email them a picture of their item and ask where to post it. The response has "status": "awaiting_address" and a claim_url; it prints the moment they reply, and unclaimed items are refunded after 21 days. |
recipients | array | Up to 500 recipient objects (each may have its own reference and arrive_on). |
arrive_on | date | YYYY-MM-DD — we hold it and post it to arrive that day. |
reference | string | Your own id (CRM contact, order number) — returned on every event. |
test | bool | true checks everything (address, do-not-mail list, credits) without sending or charging. |
allow_repeat | bool | We never post the same person twice from the same campaign unless this is true. |
Every address is checked against the national postcode list and your do-not-mail list before anything is printed.
Designs
No saved campaign? Pass a design — we keep and reuse identical designs automatically.
{
"type": "letter",
"design": {
"bespoke": true,
"purpose": "marketing",
"brief": "We fit EV chargers for businesses. Offer a free site survey.",
"headline": "A little something for {company}",
"offer": "Free site survey this month — quote your code",
"booking_url": "https://calendly.com/acme/15min",
"signoff": "Warm wishes,\nThe Acme team"
},
"recipient": { "name": "Tom Patel", "company": "Patel & Co", "address1": "5 Mill Lane",
"city": "Bristol", "postcode": "BS1 5TR", "website": "https://patelandco.co.uk" }
} | design. | What it does |
|---|---|
bespoke | true: we read each recipient's website — a realistic photo of their premises (their real building when they show it, smartened up), their logo and colours, their team (with faces: true) — and write each message or letter for them. |
purpose | marketing, welcome, thankyou or update. |
brief | What you want to say — the AI writes each message from it. |
cover_url, message | Your own artwork (https) and message for non-bespoke items. Use {first_name}, {company} or any recipient field. |
headline, signoff, colour, logo_url, company | Your branding (defaults come from your Brand kit). |
offer | Printed with a unique code per person — track customers won and revenue. |
cta_url, cta_label, booking_url | Buttons on each person's personal page (behind the QR code). |
gift | mug or magic_mug — sent with the card, printed with its design. |
GET/items/{id}
{
"id": 4512, "format": "card", "status": "posted", "status_date": "2026-10-23",
"tracking_url": "https://…", "reference": "crm-contact-8812",
"recipient": { "name": "Sarah Jones", "company": "Brightside Dental", "postcode": "GU1 3AA" },
"preview_url": "https://…", "page_url": "https://www.personalizedmarketing.co.uk/b/?t=9fk2ma7q",
"offer_code": "K7M2QX", "scans": 2, "clicks": 1, "redemptions": 1, "revenue_pence": 12000
} Statuses: designing → queued → printing / scheduled → posted (or failed, cancelled). GET/items?campaign_id=&limit=&starting_after= lists them. DELETE/items/{id} stops one that hasn't been made yet and refunds the credit.
GET/campaigns · /campaigns/{id} · /me · /automations
Your campaigns (ids to use as template_id), each campaign's live results (posted, scanned, clicks, customers won, revenue, A/B comparison), your credit balance and your automations.
Webhooks
Set your URL in the Studio (API & integrations) or with POST/webhook {"url": "https://…"}. We POST JSON for every event, signed:
X-PM-Event: card.scanned
X-PM-Signature: sha256=<HMAC-SHA256 of the raw body with your signing secret>
{ "type": "card.scanned", "created": "2026-10-24T09:12:03+00:00",
"data": { "recipient_id": 4512, "campaign_id": 88, "offer_code": "K7M2QX", "scans": 1,
"recipient": { "name": "Sarah Jones", "company": "Brightside Dental" } } } | Event | When |
|---|---|
item.created | An item is queued (API, automation or send link). |
item.posted | Printed and handed to Royal Mail. |
item.digital_ready | A digital design is ready: image_url and page_url for your own email. |
item.emailed | We emailed the digital design to the person (when email_it is on). |
card.scanned | Someone opens their personal page (QR code). |
card.clicked | They press your button or "Book a call". |
card.team_photo | They put their own team on the card. |
lead.created | They leave their details on their page. |
code.redeemed | Their offer code is used (API, Studio or Shopify). |
card.review_clicked | They press "Leave a Google review" on their page. |
card.replied | They send you a thank-you card back. |
item.claimed | Someone you sent by email gave us their address — it's going to the printer. |
item.cancelled | Not claimed in time, or they said no thanks (credits refunded). |
Answer with any 2xx. Failed deliveries are retried with back-off for about two days. Verify signatures:
$ok = hash_equals("sha256=" . hash_hmac("sha256", file_get_contents("php://input"), $secret),
$_SERVER["HTTP_X_PM_SIGNATURE"] ?? "");
GET/leads · /events
Prefer to poll? /leads?since_id= and /events?since_id= return everything after the last id you saw. Add order=desc for the newest 50 first, and /events?type=card.scanned for one kind only. Using Zapier? Our Zapier app does all of this for you.
POST/redeem
curl https://www.personalizedmarketing.co.uk/api/v1/redeem -H "Authorization: Bearer YOUR_API_KEY" -d code=K7M2QX -d value=49.99 Records a customer won (and what they spent) against the person whose card carried that code. Shopify orders that use a code as a discount are recorded automatically through your automation webhook.
No code: one-click links, automations & Zapier
A "Send card" button in any system
https://www.personalizedmarketing.co.uk/send.php?t=123&name={{contact.name}}&company={{company.name}}&address1={{contact.address}}&city={{contact.city}}&postcode={{contact.zip}}&website={{company.domain}} Add it as a custom link or button in HubSpot, Pipedrive, Salesforce, a Google Sheet or an email. It opens a filled-in form; you press Send.
Automations (Shopify, HubSpot workflows, Zapier, Make)
In the Studio, turn a campaign into an automation and paste its webhook address into Shopify (Order creation / Customer creation), a HubSpot workflow ("Send a webhook"), or a Zapier/Make "Webhooks — POST" step. Every new customer gets their card automatically.
Errors & limits
{ "error": { "type": "no_credits", "message": "Not enough credits — top up in the Studio" } } | HTTP | Meaning |
|---|---|
| 400 | Something's missing or wrong in the request. |
| 401 | Missing or wrong API key. |
| 402 | Not enough credits. |
| 404 | Not found (template, item, code). |
| 422 | The recipient can't be posted to (bad postcode, on the do-not-mail list, outside the UK). |
| 429 | More than 120 requests a minute. |
Ready to send real post from your code?
Open API & integrationsQuestions? Email hello@personalizedmarketing.co.uk.