unopost

MCP connector

Post a letter from your AI assistant

Add one URL to Claude, ChatGPT or any MCP client. Your assistant gets your original document ready to print on A4 and post by Australia Post, tracked. You check the preview and pay on our website, so nothing is posted without you.

$10 regular post$20 Express Post

Server URL
https://unopost.com.au/api/mcp

The same prices as on the website, GST included. No account, sign-in or API key.

Posting from your own code? The connector works from any MCP client or plain HTTP, but each letter is paid for on our website. To post with no one in the loop, use the x402 API.

Connect itServer card

Connect it

The connector is a remote MCP server over Streamable HTTP. It needs no sign-in, so leave authentication off.

  • Claude: open Customize → Connectors, choose Add custom connector, name it Unopost and paste the server URL. In a chat, turn Unopost on from the tools menu.
  • ChatGPT: add a custom connector in developer mode with the server URL and no authentication, then turn it on in the chat.
  • Claude Code and other clients: add it as a remote HTTP server. Any client that takes custom MCP servers uses the same URL.
Claude Code
claude mcp add --transport http unopost https://unopost.com.au/api/mcp
.mcp.json
{
  "mcpServers": {
    "unopost": {
      "type": "http",
      "url": "https://unopost.com.au/api/mcp"
    }
  }
}

How a letter goes

  1. Confirm the addresses
  2. Prepare the letter
  3. Wait for the layout
  4. The user pays on the website

The assistant confirms the addresses with the user, prepares the letter and gives the user a private review link. The user checks the preview there, enters an email and pays by card. Paying is what posts the letter.

What the user says
Post this letter by Express Post to Jane Citizen,
12 Example Street, Southbank VIC 3006.
My return address is 1 Sample Road, Carlton VIC 3053.

Tools

Assistants call these themselves. Every result from prepare_letter, finish_letter_upload and get_letter_status has reviewUrl, the private page where the user checks the preview and pays, and instructions, which say what to tell the user next.

prepare_letter

TOOLprepare_letter

Takes a file reference from the host, such as an attachment in ChatGPT, downloads the original and starts the letter. The file is never retyped, rebuilt or sent as text. Each call creates a new letter, so an assistant calls it once.

Arguments

fileobject · required
The host's reference to the user's original attachment: download_url and file_id, with file_name and mime_type if known.
fileNamestring · optional
The file's name with its extension, such as letter.pdf. Only needed when file.file_name is missing.
recipientAddress · required
Who the letter goes to, as the user confirmed it. In Australia unless service is international.
senderAddress · required
The user's return address, printed on the envelope. It can be in any country.
servicestring · optional
express (the default), flat in a B4 envelope; regular, folded into a DL envelope, up to 12 pages; or international, signed for on delivery.
signatureboolean · optional
Signature on delivery, $6 extra in Australia. Regular post with it goes by Registered Post.
duplexboolean · optional
Print double-sided. Statutory declarations are always printed single-sided.
monoboolean · optional
Print in black and white. Colour stays colour by default, at no extra cost.
stapleboolean · optional
Staple the pages once in the top-left corner. Only for documents of up to 12 pages; a longer one is rejected when it is priced. Defaults to false.
foldboolean · optional
Fold an Express Post or international letter of up to 12 pages into a smaller envelope.
preparedForstring · optional
Only when you write for a business to one of its clients: the business's name, up to 60 characters. The pay page shows it as what the assistant told us, not as checked, and names like Unopost, Australia Post, a bank or a government agency are refused.
notifyEmailstring · optional
With preparedFor: the business's email. It gets a copy of every email about the letter, from payment to delivery, and the customer sees this address before paying.
customerEmailstring · optional
The paying customer's email, if you have it. The pay page starts with it filled in; tracking emails go to it.

Address

name, line1 and suburb are required; names and lines take up to 40 characters. In Australia the postcode sets the state. Overseas, add country, and a Chinese mobile number in phone for a letter to China.

namestring · required
The person's full name.
companystring · optional
A business or organisation.
line1string · required
The street address, or a PO Box such as "PO Box 123".
line2string · optional
A second address line.
suburbstring · required
The suburb or town.
statestring · optional
Leave it out in Australia: the postcode sets it. If given, one of ACT, NSW, NT, QLD, SA, TAS, VIC or WA, matching the postcode. Overseas, the region, if the country uses one.
postcodestring · optional
Four digits, required in Australia. Overseas, the postal code, if the country uses one.
countrystring · optional
Leave it out for Australia. Otherwise the country's name in English. The recipient can be overseas only with service set to international.
phonestring · optional
Only for a recipient in China: their Chinese mobile number, which China Post needs.
tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "prepare_letter",
    "arguments": {
      "file": {
        "download_url": "https://files.example.com/letter.pdf",
        "file_id": "file_abc123",
        "file_name": "letter.pdf",
        "mime_type": "application/pdf"
      },
      "recipient": {
        "name": "Jane Citizen",
        "line1": "12 Example Street",
        "suburb": "Southbank",
        "postcode": "3006"
      },
      "sender": {
        "name": "Sam Sender",
        "line1": "1 Sample Road",
        "suburb": "Carlton",
        "postcode": "3053"
      },
      "service": "express"
    }
  }
}
structuredContent
{
  "jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
  "reviewUrl": "https://unopost.com.au/pay?job=019d7a7f-a183-752d-9b3b-11511179033b",
  "currency": "AUD",
  "status": "CONVERTING",
  "service": "express",
  "instructions": "The letter is being laid out, usually for a few seconds. Then give the user reviewUrl to check the preview, enter an email and pay. Nothing is posted until payment. …"
}

Upload the file yourself

TOOLcreate_letter_upload → finish_letter_upload

For clients that can read the user's file and make an HTTP request, but whose host has no file references. create_letter_upload takes the file name and returns uploadUrl, which expires in 5 minutes. PUT the original bytes to it, then call finish_letter_upload with the jobId and the same letter fields as prepare_letter.

PUT uploadUrl
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @letter.pdf

get_letter_status

TOOLget_letter_status

Reports where a letter is, with the final pageCount and costCents once it's laid out. A letter usually takes a few seconds. Check at most every 5 seconds.

CONVERTED
{
  "jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
  "reviewUrl": "https://unopost.com.au/pay?job=019d7a7f-a183-752d-9b3b-11511179033b",
  "currency": "AUD",
  "status": "CONVERTED",
  "pageCount": 3,
  "costCents": 2000,
  "previewUrl": "https://unopost.com.au/preview/019d7a7f-a183-752d-9b3b-11511179033b/letter.pdf?Expires=...&Signature=...",
  "service": "express",
  "instructions": "Ready: give the user reviewUrl to check the preview, enter an email and pay by card. The letter is posted only once it's paid for. pageCount and costCents (AUD cents) are final."
}
FAILED
{
  "jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
  "reviewUrl": "https://unopost.com.au/pay?job=019d7a7f-a183-752d-9b3b-11511179033b",
  "currency": "AUD",
  "status": "FAILED",
  "error": "document has more than 80 pages",
  "instructions": "Not posted, and nothing was charged. The document has more than 80 pages. Suggest splitting it into separate letters of up to 80 pages each. …"
}

get_letter_info

TOOLget_letter_info

Current prices in AUD cents, the pages each price covers, page and file limits, and plain-language answers about posting times, delivery, tracking and cancelling. Assistants use it to answer questions before preparing anything.

tools/call
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_letter_info",
    "arguments": {}
  }
}

Limits

pages
Up to 80 pages per letter. Split a longer document into separate letters.
regular
Regular post, and any folded letter, takes up to 12 pages. Longer letters go by Express Post, flat.
size
Up to 50 MB per file.
files
PDF, Word (.doc, .docx), JPEG or PNG. Word documents are laid out on A4.
letter
One file per letter through the connector. The website takes up to 10.
rate
200 letters an hour from each AI host.

Statuses

CREATED
Reserved, waiting for its file.
CONVERTING
Being laid out.
CONVERTED AWAITING_PAYMENT
Ready for the user to check and pay at reviewUrl.
PAID PAYING SENDING RETRYING MANUAL_CHECK
Paid. Letters paid for by 2:30pm Melbourne time on a business day are posted that day.
POSTED UNDELIVERED
Lodged with Australia Post. The tracking number has been emailed to the user.
DELIVERED
Australia Post's tracking shows it delivered.
FAILED
Not posted. error says why, and instructions what to do next. A paid letter is refunded in full.
PAYMENT_EXPIRED
Not paid for in time. Its document has been deleted.
REFUNDED
Cancelled or couldn't be posted, and refunded to the card.

Errors

A tool that fails returns an error result whose text says what went wrong and what to do, written for the assistant. There are no error codes, and we may reword the messages, so act on the kind of error, not its exact words.

A tool never charges or posts, so an error never costs the customer anything. But once an error names a job, that letter exists: check it with get_letter_status rather than preparing a second one.

arguments
An argument is missing or wrong: an address the user should confirm, options that don't go together, an unsupported file type, a refused preparedFor or a malformed email. The message names it. Fix it and call again; nothing was created.
file
The original file couldn't be fetched from the host: the link expired, was refused, was too slow or redirected too often, points somewhere private, or the file is empty or over 50 MB. Nothing was posted. Get a fresh file reference, or use create_letter_upload; never send a text copy instead.
job …
The message starts job …: when the letter was created but a later step failed. Call get_letter_status with that job ID, then finish_letter_upload if it's still waiting for its addresses. Don't prepare another letter.
rate limit
Too many letters from this AI host this hour, or the connector's limit for the day is reached. Don't retry in a loop: tell the user to try later, or to send it on the Unopost website.
paused
We've paused new letters for a while; letters already under way carry on. Try again later.
not found
No letter has that jobId, or its upload expired after 24 hours. Use only a job ID Unopost returned to this user, and start again if it expired.
server
Something went wrong on our side: Unopost API returned HTTP 5xx. Wait a few seconds and try once more.
FAILED
A letter that can't be printed isn't a tool error: get_letter_status returns FAILED with error and plain instructions, such as splitting a document over 80 pages or removing a password.

Payment and privacy

  • It never pays or posts. The connector returns no payment link. The user pays only on the website, after seeing the preview.
  • The original file, unchanged. Files never travel inside MCP messages, and are never replaced with text.
  • Private links. A job ID is the only key to a letter, so assistants keep it and reviewUrl to the user.
  • Cancel before printing. A paid letter can be cancelled for a full refund from reviewUrl until we start printing it.
  • Deleted after posting. Documents are deleted once a letter is posted, and an unpaid letter's after 24 hours.

Webhooks? Use an inbox

The connector has no webhooks. Instead, give the assistant a notifyEmail at an inbox your software reads. Every email about a letter comes from notifications@unopost.com.au and carries the letter in its headers, so a mail service that runs code on incoming mail, such as Amazon SES, Postmark, Mailgun or SendGrid, can act on each update as it arrives.

  1. Point notifyEmail at an address your mail service receives, such as letters@yourbusiness.com.au.
  2. Accept only mail from notifications@unopost.com.au that passes DMARC, and drop the rest.
  3. Read the headers, not the wording, which can change. Drop an Unopost-Event-Id you've already seen.

Headers

Unopost-Letter-Id
The letter's jobId, as the connector returned it.
Unopost-Event
What happened: receipt, posted, tracking, retrying, delayed, awaiting-collection, delivered, cancelled, failed, refunded or refunded-printed.
Unopost-Event-Id
The same for the same news however often it's sent, so you can drop repeats.
Unopost-Letter-Status
The letter's status after it: TO_POST, POSTED, DELAYED, AWAITING_AT_COLLECTION, DELIVERED, CANCELLED or REFUNDED.
Unopost-Tracking-Number
The Australia Post tracking number, once the letter is posted.
Unopost-Copy
business on the copy a business gets; not on the customer's own email.
Headers
From: Unopost <notifications@unopost.com.au>
Unopost-Letter-Id: 019d7a7f-a183-752d-9b3b-11511179033b
Unopost-Event: tracking
Unopost-Event-Id: evt_3f9a1c0b7e2d4a6b8c1d0e2f
Unopost-Letter-Status: POSTED
Unopost-Tracking-Number: 33LQV507012301000935005
Unopost-Copy: business
Amazon SES · receipt rule Lambda
export const handler = async (event) => {
  const { mail, receipt } = event.Records[0].ses;
  const { from } = mail.commonHeaders;
  const fromUs = from.length === 1 && /(^|<)notifications@unopost\.com\.au>?$/i.test(from[0]);
  // DMARC, not DKIM alone: only a DMARC pass means unopost.com.au signed it.
  if (!fromUs || receipt.dmarcVerdict.status !== 'PASS') return;
  const header = name =>
    mail.headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value;
  await recordUpdate({
    letterId: header('Unopost-Letter-Id'),
    event: header('Unopost-Event'),
    eventId: header('Unopost-Event-Id'), // drop one you've seen
    status: header('Unopost-Letter-Status'),
    trackingNumber: header('Unopost-Tracking-Number'),
  });
};

Copies start once the customer pays, so nothing arrives for a letter that's never paid for: check get_letter_status until then. The customer's own emails carry the same headers. We may add headers, but never rename or remove these.

Discovery

Agents can find the connector without being told. It's listed in our AI catalog, which points to its MCP server card.

Pricing

ItemPrice
Regular post, folded to fit a letterbox: up to 12 pages$10
Registered post, regular post signed for on delivery: up to 12 pages$16
Express Post, flat in a B4 envelope: 20 pages included, then $0.30 for each extra page$20
Express Post with signature on delivery: 20 pages included, then $0.30 for each extra page$26
Tracking, colour, double-sided or black-and-white printing, and staplingFree

Every letter is tracked. The final price depends on the page count, which is known once the letter is laid out.

Letters prepared through the connector are covered by our Terms and Privacy policy, like any other.