Order updates, receipts & alerts

The message they are actually waiting for.

Order confirmed. Out for delivery. Payment received. Card declined. Nobody chases a newsletter, but everybody chases an order — so these messages have to go the moment the thing happens, on a channel that suits them, exactly once, with a record you can read back afterwards.

One endpoint, three channelsDelivery state per messageSigned webhooks on every outcome

Sending login codes or two-factor prompts? Those have their own page — one-time codes and verification.

A receipt is not a newsletter.

A campaign tool is built to send a lot of messages to a lot of people at once and tell you how the batch did. A transactional message is the opposite shape: one person, one event, and the only number that matters is whether that single message landed. QuickGrow treats it that way.

Your system decides

Triggered by the event, not a schedule

Your checkout, your dispatch job, your payment handler makes one call at the moment the thing happens. There is no list to build, no campaign to set up and nobody has to remember to press send.

One id, one outcome

You can show it arrived

Every send returns the id of that one message. Read it back later for the state it reached, the time it reached it, and the reason and code if it failed — so support answers “did the receipt reach me?” from the record instead of guessing.

Safe to call twice

Your retry is not their second message

Connections drop mid-request. Send the same call again with the same retry key and QuickGrow replays the original response for 24 hours rather than sending again, so a bad network never turns into two confirmations and two charges.

From your event to their phone.

Four steps, and only the first one is set-up. After that a message is one call and everything you need to know about it is a read.

  1. Make a key, scope it

    Create an API key and give it only what the job needs. Sending, reading message status and managing webhooks are three separate permissions, so the service that sends order confirmations never carries the right to export your contacts.

  2. Call one endpoint

    Post the channel, the recipient, and either the wording inline or a template id with the values to fill in. An email send can carry files — up to ten, seven megabytes in total — so the invoice travels with the confirmation rather than in a second message.

  3. Take the outcome

    The response carries the message id and the state it is in. From there, either read that id when you need it or subscribe to webhooks and let QuickGrow tell you the moment it is sent, delivered, failed or bounced.

  4. Reconcile whenever

    List your history filtered by channel, state and date, or ask for the totals per channel over a period. Nothing depends on your endpoint being up: if a webhook never reached you, the record is still sitting there to read.

Send it. Then look it up.

One key header authenticates every request. The send is a single call; everything after it is a read against the id that call gave you.

POST /api/v1/messages
X-API-Key: mk_…

{
  "channel": "email",
  "to": "[email protected]",
  "templateId": "order_confirmed",
  "variables": {
    "name": "Rumana",
    "orderId": "A-4471",
    "eta": "Tue 27 Aug"
  },
  "attachments": [
    { "filename": "receipt.pdf",
      "content": "JVBERi0xLjQK…" }
  ],
  "metadata": { "orderId": "A-4471" }
}

# same shape for "sms" and "whatsapp"
GET /api/v1/messages/:id
{ "data": {
  "id": "msg_9fA2",
  "channel": "email",
  "status": "delivered",
  "createdAt": "2026-08-24T11:02:04Z",
  "sentAt":    "2026-08-24T11:02:07Z",
  "deliveredAt": "2026-08-24T11:02:11Z",
  "failedAt": null,
  "failureReason": null,
  "failureCode": null,
  "metadata": { "orderId": "A-4471" }
} }

# list: GET /api/v1/messages?status=failed
1,000 requests a minute per keyLimit, remaining and reset on every responseYour own order id echoed backNamed error codes, each linking to the docs
The QuickGrow API reference, with the endpoint navigator on the left and the full request and response specification beside it.
Templates and docs

The wording does not live in your code.

Write the confirmation once as a template in QuickGrow and send it by id with the values filled in. When somebody wants to reword the delivery message or add a line about returns, they edit the template — nobody waits for a release. Every endpoint, field and error is written down inside the product and published as a machine-readable specification you can import into your own tooling.

  • Reference a template by id and pass values for its placeholders; a placeholder you leave out is left as it is rather than printed as a blank.
  • Create and list templates over the API too, if you would rather keep the wording in your own repository.
  • Set the sender name and the reply-to address on an email send, so a receipt looks like it came from you and a reply reaches a person.

Nothing fails quietly.

QuickGrow will not switch channels behind your back or swallow a problem to keep a graph looking tidy. When a send fails it fails visibly: the message keeps the reason and the code, and a signed webhook lands on your endpoint so your system can decide what happens next.

  • Four events, only yours

    Sent, delivered, failed and bounced. Only the messages you sent through the API raise them, so your endpoint never gets buried under campaign traffic.

  • Signed, so you can trust it

    Each delivery is signed with a secret only you and QuickGrow hold, and you can roll that secret whenever you want. Fire a test event at a new endpoint before you rely on it.

  • Retried if you are down

    An endpoint that does not answer is tried again four more times, spread across the next half hour, and every attempt is recorded for you to look at.

  • The retry is your decision

    A failed message stops there. If you want the same update to go out on another channel, your code makes that call — which means you choose the wording, the timing and whether it happens at all.

POST https://yourshop.example/hooks
{
  "id": "evt_7c1e…",
  "event": "message.failed",
  "createdAt": "2026-08-24T11:03:02Z",
  "data": { "message": {
    "id": "msg_9fA2",
    "channel": "sms",
    "status": "failed",
    "failedAt": "2026-08-24T11:03:01Z",
    "failureReason": "…",
    "failureCode": "…",
    "metadata": { "orderId": "A-4471" }
  } }
}

# message.sent · delivered · failed · bounced
A channel comparison report showing email, WhatsApp and SMS volume side by side.
What you can read back

Every send is a row you can query.

The state of a message is not a report you wait for overnight — it is a field on the message itself. Read one by id, list them filtered by channel, state and date, or ask for the totals per channel over a period and get sent, delivered, opened, clicked, bounced and failed back in a single response.

  • Created, sent, delivered and failed times on every message, with the reason and the code behind a failure.
  • Filter your history by channel, by state and by date range, and page through it with a cursor, up to a hundred at a time.
  • Your own reference travels with the message and comes back on every status read and every webhook — attach the order id and you never have to match things up by phone number.

Pick the channel per message.

All three go out from the same endpoint and report the same way, so the choice is a field in your request rather than a second integration. What genuinely differs is what each channel needs from you before it will carry the message.

The same send endpoint, the same status reads and the same webhooks for all three. Choose per message, in your own code.
EmailSMSWhatsApp
SuitsConfirmations, receipts, invoices, statementsShort, urgent updates to a phoneReplies inside a conversation the customer started
Can carry a file, like an invoiceYesNoNo
Goes out at any hour of the nightYesNoNo
Needs consent recorded against that contact firstNoYesNo
Per-contact cap and cooldown applyNoYesYes
Only within 24 hours of their last message to youNoNoYes
Who it appears to come fromYour verified domain, with your own sender name and reply-toYour branded sender name once approved — the default sender until thenYour connected WhatsApp Business number
Status you can read back per messageYesYesYes
Delivery webhooksYesYesYes
Bring your own provider, or use the managed oneYesYesYes

The night-time hold and the per-contact caps are there because a phone at 2am is a complaint waiting to happen. They are the reason an order confirmation belongs on email and a delivery-is-here alert belongs on SMS during the day — and QuickGrow tells you plainly when a send was held rather than pretending it went.

Everything in the box.

Sending

  • One endpoint for email, SMS and WhatsApp — the same request, with the channel as a field.
  • Wording inline, or a template referenced by id with your variables filled in.
  • Up to ten files and seven megabytes on an email, so the invoice goes with the confirmation.

Knowing what happened

  • Status plus sent, delivered and failed times on every single message.
  • A reason and a code behind a failure, so a wrong number and a dead gateway do not look the same.
  • Your whole message history, filtered by channel, state and date.

Keeping it tidy

  • Scoped keys — sending, reading status and managing webhooks are separate permissions.
  • 1,000 requests a minute per key by default, with the limit, what is left and the reset time on every response.
  • A named code on every refusal, each linking to the page that explains it.

Before you wire it in.

How is this different from sending a campaign?

A campaign goes to a list on a schedule and is measured as a batch. A transactional send is one call about one event, made by your system at the moment it happens, and measured as a single message. They land in the same message history, so you can still see the whole picture — but only API sends raise webhooks, so your endpoint is not flooded by marketing traffic.

If my request times out, will the customer get two confirmations?

Not if you send a retry key with it. Repeat the same request with the same key inside 24 hours and QuickGrow replays the original response rather than sending again, so a dropped connection costs you nothing. Reuse the same key on a different endpoint, or while the first request is still running, and you get a clear conflict back instead of a surprise.

Can I attach the receipt as a PDF?

Yes, on email. Send the file with the message, up to ten files and seven megabytes in total once decoded. Go over that and the call comes back straight away with an error that names the limit, rather than being accepted and then failing somewhere you never look. SMS and WhatsApp sends cannot carry files, so link to the document instead.

Send the message they are waiting for.

Create an account, make a scoped key, and post your first order confirmation. One endpoint for three channels, and a record of every message you can read back.