Skip to main content
touchdrop

Trigger postcards from your own system with the TouchDrop API

You don't need Shopify or Klaviyo to use TouchDrop. If your customers live in a CRM, a subscription platform or your own database, your system can tell TouchDrop who to mail and when. TouchDrop handles the artwork, printing, postage, address checks, suppression and the unique discount code on each card.

A subscription publisher has triggered cards for its Australian subscribers from its own systems through this endpoint since the end of July 2026.

What you build

Two calls from your system, and one read when you need it.

  1. Send us a person. One request per person, when an event happens in your system or on a schedule you choose. TouchDrop posts nothing without it.
  2. Tell us when that person comes back. A purchase, a reactivated subscription, whatever counts as a return for you. Without it, your reporting shows what a campaign cost and no revenue against it.
  3. Ask what happened to a person. Optional. The answer confirms whether a card was produced and returns the discount code printed on it, which your checkout needs if you have no connected store.

Before you start

  • Create a campaign in TouchDrop and choose Webhook Trigger. Its Trigger URL appears on the campaign page. TouchDrop reviews each new campaign once before its first cards print.
  • If you will report orders, generate an API token in Settings, on the Integrations tab, under API access.
  • Set your website in Settings, on the Brand tab, so the QR code on each card has a page of yours to land on.

Authentication

There are two tokens. Treat both like passwords: they can queue printed, paid-for mail.

TokenStarts withScopeWhere you find it
Campaign trigger tokenwht_One campaignThe campaign page: the last part of the Trigger URL
API tokentda_Your whole account, for conversions and refundsSettings, Integrations tab, API access

Send the token in a header:

POST /api/webhooks/campaign-trigger HTTP/1.1
Host: touchdrop.com.au
Authorization: Bearer wht_your_token
Content-Type: application/json

On the trigger and status endpoints, X-TouchDrop-Token: wht_your_token works as well, for platforms that reserve the Authorization header.

If your platform cannot set headers, put the token in the path instead: POST /api/webhooks/campaign-trigger/wht_your_token. Prefer the header. A token in a URL ends up in access logs, proxies and browser history along the way.

Server only

Call the API from your server. Never put a token in a storefront theme, a browser script or a mobile app, where anyone can read it. Rotating a token in TouchDrop stops the old one at once; update your system with the new value.

Trigger a postcard

POST https://touchdrop.com.au/api/webhooks/campaign-trigger

Enrols one person into one campaign. The campaign already holds the artwork, the offer and the sending rules; this call says who receives it.

FieldTypeRequiredNotes
emailstringYesValid email, up to 320 characters. Also the key that stops duplicates
first_namestringNoUp to 100 characters. firstName and firstname also work
last_namestringNoUp to 100 characters. lastName and lastname also work
external_idstringNoYour own ID for this person, up to 200 characters
sourcestringNoA label for your reporting, such as winback-flow, up to 50 characters
addressobjectYes, with one exceptionSee the table below. You can leave it out when TouchDrop can find an address from your connected Shopify store or from a previous card to this person

If your platform cannot send a nested object, send the address fields at the top level instead (address1, address2, city, state, postcode or zip, country).

Address fields by account region:

AccountRequiredNotes
Australiaaddress1, city, state, postcodestate is NSW, VIC, QLD, WA, SA, TAS, ACT or NT. Postcode has 4 digits
United Kingdomaddress1, city, postcodecity is the post town. No county needed. Country GB if you send one
Netherlandsaddress1, city, postcodeaddress1 holds street and house number. Postcode in the form 1012 AB

Example request:

curl -X POST https://touchdrop.com.au/api/webhooks/campaign-trigger \
  -H 'Authorization: Bearer wht_your_token' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "jane@example.com.au",
    "first_name": "Jane",
    "external_id": "customer_123",
    "address": {
      "address1": "12 Example Street",
      "city": "Fitzroy",
      "state": "VIC",
      "postcode": "3065",
      "country": "AU"
    }
  }'

Example response (202 Accepted):

{
  "status": "queued",
  "entryId": "3f2b8c1e-0d4a-4b8e-9c61-2a7f5e9d1b40",
  "campaignStatus": "active",
  "monthlyAllowance": { "cap": 500, "used": 12, "remaining": 488 },
  "requestId": "req_a1b2c3d4e5f60718"
}

Store the entryId against your customer record. You need it to check the outcome later.

Codestatus or codeMeaning
202queuedAccepted. Processing starts within about 5 minutes
200duplicate_pendingThis person is already queued for this campaign. Nothing to do
200already_mailedThis campaign already mailed this person
400The payload is invalid. details names each field
401unauthorizedMissing, wrong or rotated token
409The campaign is completed, stopped or archived
422invalid_addressThe address cannot produce a card. Nothing was queued
422wrong_regionThe address is outside your account's country
422undeliverable_addressA parcel locker or parcel collection point
422monthly_cap_reachedYour plan's monthly postcard allowance is used up
429Too many requests. Wait for Retry-After

What a 202 tells you. TouchDrop accepted the person. If your account has no other source for the address, it also means the address passed the check, because an unusable one gets a 422 instead. A 202 does not confirm a printed card: suppression, frequency caps and the monthly allowance apply during processing a few minutes later. The status endpoint confirms the outcome.

While a campaign is in draft, awaiting review or paused, triggers still return 202 and the response carries a notice. A campaign that has never gone live holds its queue until it does. Once a campaign has gone live, a queued person who waits more than 7 days for it to resume (for example after the postage balance ran out) expires without a card, and TouchDrop alerts you.

Treat any response outside 200 and 202 as a failed action. Many platforms report only the status code, so route failures to a queue someone reviews.

Check what happened to a person

GET https://touchdrop.com.au/api/webhooks/campaign-trigger/status?entry_id=3f2b8c1e-0d4a-4b8e-9c61-2a7f5e9d1b40

Use the campaign trigger token, in the header as above. You can look up by email instead of entry_id, but prefer the ID: an email in a query string travels into proxy and CDN logs.

{
  "entryId": "3f2b8c1e-0d4a-4b8e-9c61-2a7f5e9d1b40",
  "email": "jane@example.com.au",
  "entryStatus": "processed",
  "skipReason": null,
  "receivedAt": "2026-10-01T10:00:02Z",
  "processedAt": "2026-10-01T10:04:11Z",
  "campaignStatus": "active",
  "mailing": {
    "status": "shipped",
    "scheduledAt": "2026-10-01T10:04:11Z",
    "shippedAt": "2026-10-02T09:00:00Z",
    "isHoldout": false
  },
  "discount": {
    "code": "WB-4KD2",
    "status": "active",
    "expiresAt": "2026-12-01T00:00:00Z"
  }
}

shipped means the card was printed and handed to the postal service on that date. Letter post is not tracked to the door, so no one can confirm the day it arrived. If you have no connected store, read discount.code and load it into your checkout, or your checkout will reject the code printed on the card.

skipReasonMeaning
no_valid_addressNo usable address
wrong_regionThe address is outside your account's country
undeliverableA parcel locker or collection point
suppressedYour suppression or frequency rules excluded this person
already_mailedThis campaign already mailed them
convertedThey already converted, so the campaign stopped
expiredThey waited more than 7 days for a paused campaign

Other skip reasons can appear. Treat one you don't recognise as information and keep processing.

Report a conversion

POST https://touchdrop.com.au/api/webhooks/conversion

Use your API token (tda_). You need this call when your orders do not come from a store connected to TouchDrop.

FieldTypeRequiredNotes
order_idstringYesYour ID for the order, invoice or subscription, up to 200 characters. Counted once
valuenumber or stringYesThe total the customer paid: after discounts, including tax and shipping. A decimal amount such as 120.50, never pence or cents
emailstringOne of these threeThe email you sent in the trigger
discount_codestringOne of these threeThe code from the card, if used
addressobjectOne of these threeSame shape as the trigger
currencystringNoISO code such as AUD. Defaults to your account currency. TouchDrop does not convert currencies
occurred_atstringNoISO 8601 time of the order, up to 90 days back. Defaults to now
typestringNoone_time (default), subscription_initial or subscription_renewal
source_systemstringNoOnly if two of your systems number orders the same way. Letters, numbers, dot, dash and underscore, up to 50 characters
curl -X POST https://touchdrop.com.au/api/webhooks/conversion \
  -H 'Authorization: Bearer tda_your_token' \
  -H 'Content-Type: application/json' \
  -d '{
    "order_id": "SUB-1001",
    "email": "jane@example.com.au",
    "value": 120.50,
    "currency": "AUD",
    "type": "subscription_initial"
  }'
{ "status": "attributed", "orderId": "SUB-1001", "revenueTracked": 120.5, "requestId": "req_9f8e7d6c5b4a3921" }

How an order counts towards a campaign. It counts when someone uses the discount code printed on the card, at any time. It also counts when the email or delivery address matches someone the campaign mailed, inside the campaign's attribution window: 60 days from the card's estimated arrival, unless agreed otherwise. Each order counts towards one campaign at most. Send every order; TouchDrop decides which ones attribute.

CodestatusMeaning
202attributedMatched to a campaign. revenueTracked is the amount credited
202recorded_unattributedAccepted, matched nothing. Expected for orders that did not come from a card
200duplicateThis order_id has already attributed. The first value stands
400Invalid payload, or none of email, discount_code and address
401unauthorizedMissing, wrong or rotated token
429Too many requests. Wait for Retry-After

If your account also has a connected store, the response carries a warnings entry: TouchDrop already imports that store's orders, so reporting the same sale here under another ID counts it twice.

For subscriptions, use subscription_initial for the reactivation the card won and subscription_renewal for the rebills after it. That keeps revenue the card created apart from revenue you were already collecting.

Report a refund

POST https://touchdrop.com.au/api/webhooks/conversion/refund

Use your API token and send { "order_id": "SUB-1001" }. If you sent source_system with the conversion, send the same value here.

A refund removes the whole conversion from revenue and keeps the record. Repeat calls change nothing. An order that never attributed returns 404, because TouchDrop does not store unmatched conversions. This version does not support partial refunds.

Errors

Error responses are JSON with an error message. Where it applies you also get a stable code and a details list. Branch on code, never on the wording of error; TouchDrop may reword messages and will not change codes. Responses carry an X-Request-Id header. Quote it when you contact support.

CodeRetry?
400, 401, 404, 409, 422No. Fix the request first
429Yes, after Retry-After
500Yes, with exponential backoff

Idempotency

CallKeyOn repeat
Triggercampaign + emailduplicate_pending or already_mailed. Nobody receives a second card
Conversionaccount + order_idduplicate once the order has attributed. TouchDrop counts the revenue once
Refundaccount + order_idNo change

Retries are safe. There is no batch endpoint: send one person per request, in a loop.

Rate limits

TouchDrop limits how many requests a caller can send per minute. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers, so a large import can pace itself. On a 429, honour Retry-After. If you plan a one-off bulk release, tell TouchDrop in advance.

Sending from a marketing platform

Most marketing and CRM platforms can call a webhook when someone enters a segment or a flow step fires. In Klaviyo, add a webhook step to a flow:

  • Method: POST
  • URL: https://touchdrop.com.au/api/webhooks/campaign-trigger
  • Headers: Authorization: Bearer wht_your_token and Content-Type: application/json
{
  "email": "{{ person.email }}",
  "first_name": "{{ person.first_name|default:'' }}",
  "last_name": "{{ person.last_name|default:'' }}",
  "source": "klaviyo-winback-flow",
  "address": {
    "address1": "{{ person|lookup:'$address1'|default:'' }}",
    "address2": "{{ person|lookup:'$address2'|default:'' }}",
    "city": "{{ person|lookup:'$city'|default:'' }}",
    "state": "{{ person|lookup:'$region'|default:'' }}",
    "postcode": "{{ person|lookup:'$zip'|default:'' }}",
    "country": "AU"
  }
}

If TouchDrop is connected to your Shopify store, you can leave the address out and send email and name: TouchDrop then uses an address from the customer's recent Shopify orders. Otherwise check that every field holds a value for the people in that flow. Most platforms send a missing field as an empty string, which reaches TouchDrop as a blank address and returns a 422.

Going live

  1. Create the Webhook Trigger campaign and copy its Trigger URL.
  2. Send one trigger with a real address you control. Expect 202 and store the entryId.
  3. Send one with a broken address (no postcode) and a different email. Expect 422 with code: "invalid_address". This proves your system sees failures.
  4. Poll the status endpoint until entryStatus is processed, then read mailing.status and discount.code.
  5. Post a test conversion for that person. Expect recorded_unattributed until the card's estimated arrival date, then attributed. Refund the test order afterwards so it does not count as revenue.
  6. Release a small group first, check the count your system sends against what TouchDrop accepted, then switch on the full flow. Ask TouchDrop for a hard campaign ceiling if you want one.

Data handling

DataWhy TouchDrop needs it
Postal addressTo print and deliver the card
NameTo personalise the card. Optional
EmailTo recognise the same person across calls and match orders
external_idOptional. Stored so you can reconcile with your system
Order value and IDTo attribute revenue to a campaign

TouchDrop stores each request against its campaign for processing and audit, and drops any field this page does not list. Do not send payment details or special-category data; no endpoint accepts them. TouchDrop never sells your customer data and shares it only with its print partner to produce your cards. For a data processing agreement or a deletion request, email support@touchdrop.com.au.

Frequently asked questions

Can I trigger postcards from my own CRM or app?

Yes. Create a campaign with the Webhook Trigger, then have your system send one request per person, with email, name and address, to that campaign's trigger URL. TouchDrop checks the address, applies your sending rules, prints the card and posts it.

Do I need Shopify or Klaviyo to use the API?

No. Without a connected store, send the address in every trigger, read the discount code from the status endpoint into your checkout, and report orders through the conversion endpoint.

Does a 202 response mean the card was printed?

No. It means TouchDrop accepted the person. Suppression and frequency rules run a few minutes later; the status endpoint tells you whether a card was produced and when it was handed to the post.

Can I send a whole list in one call?

No. Send one person per request. For a one-off list, a CSV upload in the app is the simpler route.

Does the API work for Australian, UK and Dutch accounts?

Yes. UK and Dutch accounts use touchdrop.co and Australian accounts use touchdrop.com.au. The address fields differ by country, as in the table above; each account mails within its own country.

API version 1.1. New response fields, optional request fields and new skip reasons can appear without notice; build your system to ignore fields it does not know. TouchDrop announces breaking changes in advance.

Still have questions? Email support@touchdrop.com.au