Wow! Build your WhatsApp integration. Start free. Register Now
WhatsApp API for Files

A receipt file in a WhatsApp chat after payment

The payer gets the receipt file instead of waiting for staff to attach it. When payment succeeds, your backend can upload that file and send it through Whapi.Cloud. This use case shows integrators how to implement that flow through the WhatsApp API.

Triggered by your payment webhook Personal chat, not a group
A paid checkout beside a phone whose WhatsApp chat shows the receipt file the payer received.
Use case

When to use this flow

Payment has already succeeded

Use this flow when the trigger is a completed payment, not an abandoned cart.

The receipt is one personal chat

Use this flow when the payer should receive the receipt as a file in a personal chat, not a group file and not a group invite.

Your service already holds the file

Use this flow when your payment webhook and your own backend already have the receipt file and the payer's number.

WhatsApp chat on a phone showing the receipt file the payer received.
Customer experience

The payer gets the file in chat

Before automation, staff download the receipt and send the file in WhatsApp by hand, so the buyer waits and some receipts are never sent.

After the flow runs, the payer sees the receipt file in their personal WhatsApp chat. Integrators build this result with the WhatsApp API.

Workflow

From a paid order to a receipt file

1

Payment succeeded

Your backend receives the payment-succeeded webhook and already holds the receipt file and the payer's number. Whapi.Cloud does not take the payment.

2

Upload the receipt file

POST /media

uploadMedia stores the receipt file and returns the media id the next call uses.

3

Send the receipt document

POST /messages/document

sendMessageDocument sends that document to the payer's number in a personal chat.

Need delivery tracking? A successful send does not mean the payer already has the receipt file. Learn about delivery status →

Prerequisites

Before you run the flow

Connect the WhatsApp number that sends the receipt, and authenticate every request with that number's API token. The payment webhook and the receipt file stay on your backend.

Optional — go deeper for this flow

API implementation

Calls that send the file

Your backend already has the paid order, the receipt file, and the payer's number. Store the file first, then send it as a document to that personal chat.

Get your API token for free after you register on whapi.cloud, and use that token in the code instead of Your_Token from the examples.

Upload the receipt file

Store the receipt file and take the media id the document send needs.

curl --request POST \
     --url https://gate.whapi.cloud/media \
     --header 'accept: application/json' \
     --header 'authorization: Bearer Your_Token' \
     --header 'content-type: application/pdf' \
     --data-binary @receipt.pdf
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://gate.whapi.cloud/media",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => file_get_contents("receipt.pdf"),
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "authorization: Bearer Your_Token",
    "content-type: application/pdf"
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests

url = "https://gate.whapi.cloud/media"
headers = {
    "accept": "application/json",
    "authorization": "Bearer Your_Token",
    "content-type": "application/pdf"
}

with open("receipt.pdf", "rb") as receipt:
    response = requests.post(url, data=receipt, headers=headers)

print(response.text)
import { readFile } from 'node:fs/promises';

const body = await readFile('receipt.pdf');
const response = await fetch('https://gate.whapi.cloud/media', {
  method: 'POST',
  headers: {
    accept: 'application/json',
    authorization: 'Bearer Your_Token',
    'content-type': 'application/pdf',
  },
  body,
});

console.log(await response.text());
OkHttpClient client = new OkHttpClient();

RequestBody body = RequestBody.create(new File("receipt.pdf"), MediaType.parse("application/pdf"));
Request request = new Request.Builder()
  .url("https://gate.whapi.cloud/media")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("authorization", "Bearer Your_Token")
  .build();

Response response = client.newCall(request).execute();
using RestSharp;

var options = new RestClientOptions("https://gate.whapi.cloud/media");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("authorization", "Bearer Your_Token");
request.AddHeader("content-type", "application/pdf");
request.AddParameter("application/pdf", File.ReadAllBytes("receipt.pdf"), ParameterType.RequestBody);
var response = await client.PostAsync(request);

Console.WriteLine("{0}", response.Content);
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	file, _ := os.Open("receipt.pdf")
	defer file.Close()
	req, _ := http.NewRequest("POST", "https://gate.whapi.cloud/media", file)
	req.Header.Set("accept", "application/json")
	req.Header.Set("authorization", "Bearer Your_Token")
	req.Header.Set("content-type", "application/pdf")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}

Send the receipt document

Send that receipt as a document to the payer's personal chat.

curl --request POST \
     --url https://gate.whapi.cloud/messages/document \
     --header 'accept: application/json' \
     --header 'authorization: Bearer Your_Token' \
     --header 'content-type: application/json' \
     --data '
{
  "to": "447123456789",
  "media": "MEDIA_ID",
  "filename": "receipt.pdf"
}
'
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://gate.whapi.cloud/messages/document",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"to\":\"447123456789\",\"media\":\"MEDIA_ID\",\"filename\":\"receipt.pdf\"}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "authorization: Bearer Your_Token",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests

url = "https://gate.whapi.cloud/messages/document"
payload = {
    "to": "447123456789",
    "media": "MEDIA_ID",
    "filename": "receipt.pdf"
}
headers = {
    "accept": "application/json",
    "authorization": "Bearer Your_Token",
    "content-type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)
print(response.text)
const response = await fetch('https://gate.whapi.cloud/messages/document', {
  method: 'POST',
  headers: {
    accept: 'application/json',
    authorization: 'Bearer Your_Token',
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    to: '447123456789',
    media: 'MEDIA_ID',
    filename: 'receipt.pdf',
  }),
});

console.log(await response.text());
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"to\":\"447123456789\",\"media\":\"MEDIA_ID\",\"filename\":\"receipt.pdf\"}");
Request request = new Request.Builder()
  .url("https://gate.whapi.cloud/messages/document")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("authorization", "Bearer Your_Token")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
using RestSharp;

var options = new RestClientOptions("https://gate.whapi.cloud/messages/document");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("authorization", "Bearer Your_Token");
request.AddHeader("content-type", "application/json");
request.AddJsonBody(new { to = "447123456789", media = "MEDIA_ID", filename = "receipt.pdf" });
var response = await client.PostAsync(request);

Console.WriteLine("{0}", response.Content);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{"to":"447123456789","media":"MEDIA_ID","filename":"receipt.pdf"}`)
	req, _ := http.NewRequest("POST", "https://gate.whapi.cloud/messages/document", bytes.NewReader(body))
	req.Header.Set("accept", "application/json")
	req.Header.Set("authorization", "Bearer Your_Token")
	req.Header.Set("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
Delivery

Know when the receipt was delivered

A successful send response does not mean the payer already has the receipt. The first status visible before a webhook is pending. Later status changes arrive only on statuses.post and statuses.put. If the status stays pending and does not move to sent, delivered, or a later ack, the message was not delivered. Check the payload and the account's permission to send that type.

  • A response from sendMessageDocument is not proof the payer has the file.

  • pending that never moves on means the receipt was not delivered.

  • Watch statuses.post and statuses.put for later acks.

What to read on delivery and webhooks

Contact our support if something went wrong
When things break

Handle failures at each step

If the document send is rejected or not delivered, the payer does not get the receipt. Retry that send or alert the team, and do not count the receipt as delivered.

Payment succeeded, but the receipt failed?

The document send can be rejected or not delivered after payment has already succeeded. Retry sendMessageDocument or alert the team, and do not count the receipt as delivered. Clients often mention that our support is responsive. Do not hesitate to ask for help.

Contact our support if something went wrong

Troubleshoot by API step

Status What it means What to do
400 Wrong request parameters
401 Need channel authorization for upload media
500 Internal Error
Status What it means What to do
400 Wrong request parameters
401 Need channel authorization for send message
402 Trial version limit exceeded
403 It is forbidden to send to this group/recipient
404 Media with specified id not found
413 Request media too large
415 Unsupported media type
429 Too many requests
500 Internal Error
FAQ

Questions integrators ask

No. The receipt goes to a personal chat. It is not a file to an existing group.
No. The payload is the receipt file, not a group invite.
If the document send is rejected or not delivered, retry that send or alert the team and do not count the receipt as delivered.
No. The trigger is a completed payment, not an abandoned cart.
No. You upload the receipt you already have and send that document. The send does not wait on a pre-written message body, and it does not add a charge for that body.
Supported WhatsApp Features

Everything you need to build WhatsApp automation

Whapi.Cloud gives you access to a complete set of WhatsApp features — messaging, media, groups, channels, webhooks and more. Build bots, automations and integrations without feature limitations.

Chosen by developers

Trusted by 3,300+ developers and SaaS teams

Developers worldwide rely on Whapi.Cloud to build automations, bots, CRM integrations, bulk messaging tools and advanced WhatsApp workflows. Our infrastructure processes millions of API requests with high stability — and grows every month.

Icon of feature

5,5K+

active API channels

Icon of feature

21M+

API requests processed monthly

“Everything is simple, intuitive and easy to navigate. The docs are clear, and all the info I need is right on the screen. Great job.”

roberto k. / Web Developer

“We connected WhatsApp to our workflows using n8n in just a few minutes. No code, no complications- everything worked on the first try.”

Martin p. / Developer

“Whapi.Cloud became a key part of our customer communication. We automated product updates, order notifications and support messages without needing Meta templates or approvals”

Oscar T. / CTO

Decorative bubble