FOR AI AGENTS

MCP Tool Reference

The five tools the Preznt MCP server exposes, and what each one takes and gives back. New here? Start with the Agent Quickstart — it covers connecting your client and the order flow end to end.

One endpoint, no API key: https://api.preznt.net/mcp (Streamable HTTP). Direct REST access is not a supported interface.

HOW RESULTS COME BACK

  • ✅ A successful call returns the JSON below as the tool result text — pretty-printed, nothing wrapped around it.
  • ⚠️ A failed call is returned as an error result, prefixed with the status: HTTP 422: {"error": "..."}. The message is meant for you to act on — it names the field or rule that was wrong, so read it and correct the call rather than retrying it unchanged.
  • 🧪 Every rule below applies identically in test and production mode. Mode changes who pays, never what validates.
  • 💐 In production the payment is real, your user is charged, and the flowers are really delivered — placed with a florist by hand, refunded in full if we cannot fulfil (terms). In test nothing is charged and nothing is delivered.

search_products

List the products Preznt can deliver, with optional filters. Start here — an order needs a product_id, and each product is delivered in exactly one country.

PARAMETERS

Name Type Required Notes
country "US" | "GB" | "UK" | "DE" | "CH" | "IT" no Only products delivered in this country. "UK" and "GB" mean the same thing. Omit to list every country.
category "flowers" no Filter by product category. Flowers is the only category today.
max_price_cents integer no Only products at or under this price, in the product's own minor units — US cents for a USD product, pence for a GBP one. There is no conversion between the two.
max_delivery_days integer no Only products whose minimum lead time is at most this many days.

RETURNS

{
  "products": [
    {
      "id": "prod_ABC123",
      "name": "Signature Flower Bouquet (USA)",
      "description": "...",
      "category": "flowers",
      "price": { "cents": 10000, "currency": "USD", "formatted": "$100.00" },
      "deliveryCountry": "US",
      "supplier": "manual",
      "delivery": { "daysMin": 14, "daysMax": 90 },
      "imageUrl": "https://..."
    }
  ],
  "count": 1
}

GOOD TO KNOW

  • A country Preznt does not deliver to returns an empty list with count 0 — not an error. "Nothing" is the honest answer to "what can you send to Ireland?".
  • price.cents is minor units of price.currency. Never compare a USD cents figure with a GBP pence one, and never convert between them yourself.
  • recipient.country in create_order must match the deliveryCountry of the product you pick, so filter by country before choosing.

get_product

Fetch one product by ID — its price, the currency it is charged in, the country it is delivered in and its lead time. Useful to re-check a product ID before ordering.

PARAMETERS

Name Type Required Notes
product_id string yes Product ID from search_products, e.g. prod_ABC123.

RETURNS

{
  "id": "prod_ABC123",
  "name": "Signature Flower Bouquet (USA)",
  "description": "...",
  "category": "flowers",
  "price": { "cents": 10000, "currency": "USD", "formatted": "$100.00" },
  "deliveryCountry": "US",
  "supplier": "manual",
  "delivery": { "daysMin": 14, "daysMax": 90 },
  "imageUrl": "https://..."
}

GOOD TO KNOW

  • An unknown or inactive product ID is a 404 — the tool result comes back as an error, not an empty object.
  • delivery.daysMin is the supplier lead time, not the ordering rule: a delivery_date still has to be at least 3 business days and at most 3 months out, on a weekday, and off the delivery country’s public-holiday list.

create_order

Create a gift order. It comes back PENDING, never accepted: only the human behind the contact details you supply can move it on, by acting on a link sent to them.

PARAMETERS

Name Type Required Notes
product_id string yes Product ID from search_products.
mode "test" | "production" yes No default. "test" sends a confirmation link and charges nothing. "production" sends a Stripe payment link and charges the real price. Use "test" unless the user has actually asked for a real, paid order.
payer_email string yes, unless payer_phone is given on a UK order Email address of the human placing the order — they receive the confirmation or payment link. Bare address only (name@domain.tld), no display name or angle brackets. Reserved example domains (example.com, .test, .invalid, .example, .localhost) are rejected. Need not be in the delivery country.
payer_phone string no (UK orders only) UK mobile of the human placing the order — 07xxx xxxxxx or +447xxx xxxxxx. Only accepted when recipient.country is GB/UK; rejected with a 400 on a US, German, Swiss or Italian order. A landline is rejected. At least one of payer_email / payer_phone is required.
payer_name string (≤100) no Name of the human placing the order, used to greet them in the email.
delivery_date string (YYYY-MM-DD) yes At least 3 business days and at most 3 months from today — weekends and the delivery country’s public holidays do not count towards the three. On a weekday, and not a public holiday in the delivery country (US federal holidays, UK bank holidays, or the German, Swiss or Italian calendar; the German and Swiss lists are the union of their regional holidays).
recipient object yes Where the flowers go. A street address in the US, the UK, Germany, Switzerland or Italy — never a PO Box (Postfach, Packstation and casella postale included).
↳ name string (≤100) yes Recipient name.
↳ address string (≤200) yes Street address. A PO Box is rejected — flowers need a doorstep — and so are its local equivalents (Postfach, Packstation, case/casella postale).
↳ city string (≤100) yes City, or the UK post town / Italian comune.
↳ country "US" | "GB" | "UK" | "DE" | "CH" | "IT" no (defaults to "US") Delivery country; must match the product’s deliveryCountry. "UK" means "GB"; each country also answers to its own name (Deutschland, Suisse, Italia…).
↳ state string (≤100) US: yes / elsewhere: no US: the 2-letter state code — one of the 50 states or DC. Territories (PR, VI, GU, AS, MP) and military APO/FPO (AA, AE, AP) are not served. Elsewhere: the optional county, Bundesland, canton or provincia, which is stored but not validated.
↳ zip string (≤20) yes US: a real zip located in the state given above — one belonging to another state is rejected, and the error names it. UK: the full postcode, e.g. SW1A 1AA (case and spacing are normalized for you). DE: the five-digit PLZ, e.g. 10115. CH: the four-digit PLZ/NPA, e.g. 8001 — Liechtenstein (9485–9498) and the Campione d’Italia and Büsingen exclaves are refused by name. IT: the five-digit CAP, e.g. 00184 — San Marino and Vatican City are refused by name.
gift_message string (≤255) no Message on the gift card. At most 255 characters — the florist’s card cannot carry more, so a longer message is refused rather than cut off.

RETURNS

{
  "order_id": "ord_XYZ789abc",
  "mode": "test",
  "status": "pending_confirmation",
  "amount": { "cents": 10000, "currency": "USD", "formatted": "$100.00" },
  "delivery_country": "US",
  "confirmation_email": { "sent": true, "to": "al•••@their-domain.com" },
  // present only if you supplied payer_phone (UK orders)
  // "confirmation_sms": { "sent": true, "to": "+4477•••••321" },
  "next_step": "al•••@their-domain.com must open the confirmation link sent to
                them to move the order to \"accepted\". Nothing is charged.",
  "message": "Order created and waiting on the human. ..."
}

GOOD TO KNOW

  • The confirmation/payment link is deliberately never returned to you. Tell the user to check their email (including spam), then poll get_order_status.
  • A pending order is not an error and not a failure — re-running create_order does not retry it, it creates a second order and consumes another slot of the per-payer limit.
  • On a send failure the channel object carries failure and retryable. retryable: true means the address or number is the problem (suppressed, invalid_destination, opted_out, unconfirmed_limit) — ask the user for a different one. retryable: false is a Preznt-side fault; ordering again fails identically, so stop and email hello@preznt.net with the order ID.
  • The order is stuck only if every channel you gave failed — one working channel is enough.
  • Fulfilment capacity: at most 500 paid orders may be waiting to be placed service-wide. While that queue is full, production orders come back 503 with retryable: true — nothing is charged and no order is created. It is not a per-caller limit; the orders are placed by hand, so it clears at human speed. test orders are never refused for this reason.
  • Orders cannot be cancelled once submitted.

get_order_status

Poll an order by ID. This is how you find out whether the human confirmed or paid — nothing is pushed back to you.

PARAMETERS

Name Type Required Notes
order_id string yes Order ID from create_order, e.g. ord_XYZ789abc.

RETURNS

{
  "order_id": "ord_XYZ789abc",
  "status": "pending_confirmation",
  "mode": "test",
  // present only while the order is waiting on the human
  "waiting_on": "Waiting for the human to open the confirmation link emailed
                 to them. Nothing is charged.",
  "product": { "name": "Signature Flower Bouquet (USA)", "category": "flowers" },
  "delivery_date": "2026-09-21",
  "delivery_country": "US",
  "recipient_name": "Jane Doe",
  "confirmation_email_sent": true,
  "confirmation_sms_sent": false,
  "tracking_url": null,
  "created_at": "2026-09-04T10:15:00.000Z"
}

GOOD TO KNOW

  • An unknown order ID returns status "unknown" with HTTP 200, not a 404 — so a typo in the ID looks like a missing order, not a broken call.
  • An order sitting at pending_confirmation or pending_payment is waiting on the human, not on Preznt. Back off and poll; do not re-create the order.

create_support_ticket

File a ticket for a human to pick up — a missed delivery, the wrong item, a payment problem, or an order that came back "failed". Response promised within 24 hours.

PARAMETERS

Name Type Required Notes
order_id string no Related order ID, if any. An unknown ID is a 404.
contact_email string (email) this or contact_phone Where support should reply.
contact_phone string this or contact_email For the support team's own reference — no text is ever sent when a ticket is created.
issue_type "not_delivered" | "wrong_item" | "payment" | "quality" | "other" yes What kind of problem this is.
description string (10–2000) yes What happened, in detail.

RETURNS

{
  "ticket_id": "tkt_ABC123",
  "status": "open",
  "message": "Support ticket created. We'll respond within 24 hours."
}

GOOD TO KNOW

  • Rate limited per IP, and more tightly than every other tool — so do not file one per poll.

ERRORS

Status Meaning What to do
400 Validation failed A field is missing or malformed — details names the field. Fix the argument and call again.
403 Mode unavailable test orders are switched off, or the delivery country is not open for production. Not retryable as-is.
404 Not found No such product, order or ticket. Re-check the ID with search_products or get_product.
422 Rule violated The date or address broke an ordering rule — the message names the rule, and a lead-time error names the earliest possible date.
429 Rate limited Wait the number of seconds in Retry-After before trying again.
502 / 503 Upstream, config or capacity fault Payment setup failed, production ordering is not configured, or Preznt is at fulfilment capacity (500 paid orders already waiting to be placed). No order row is created and nothing is charged — tell the user rather than looping. A capacity 503 carries retryable: true and a Retry-After of an hour, and test orders still work.

Rate limits: 50 orders per payer email address/24h, 60 requests/minute. Other limits apply per IP and are not published. See the quickstart for the full ordering rules, statuses and contact/consent policy.

NEXT