API documentation

PProMM verifies mobile wallet payment receipts (KPay, KBZPay, Wave, AYA Pay, MMQR and more) and returns one of three verdicts: approved, pending review or rejected — with a machine readable reason.

Overview

Base URL
All endpoints are relative to this host.
https://ppromm.com

Machine readable spec (OpenAPI 3.1) — import into Postman, Insomnia, Swagger UI or a client generator:

https://ppromm.com/api/public/openapi.json
Download OpenAPI JSON
  1. Register your app in the PProMM console (Clients) and copy the API key and webhook secret.
  2. When your customer uploads a receipt, POST the image plus what you expected to be paid.
  3. Act on the verdict. If it is pending_review, poll the status endpoint or wait for the signed webhook.

Authentication

Send your API key in the x-api-key header on every request. Keys start with rv_ and must never be exposed in browser code — call PProMM from your backend.

x-api-key: rv_live_xxxxxxxxxxxxxxxxxxxx

A missing, invalid, or deactivated key returns 401 Invalid or inactive API key.

POST /api/public/verify-receipt

Submits a receipt image for verification. Responds synchronously with the verdict.

Request body
FieldTypeNotes
image_base64stringRaw base64 or a data URL. Required unless image_url is given. Max 10MB decoded.
image_urlstringPublicly reachable image URL. Alternative to image_base64.
expected_amountnumberAmount your app expects, e.g. 25000.
currencystringDefaults to MMK.
expected_receiverstringMerchant name or account the money must arrive at.
walletstringOptional hint: kpay, kbzpay, wavepay, ayapay, mmqr…
referencestringYour own order/invoice id, echoed back as reference.
callback_urlstringOverrides the app's default webhook URL for this submission.
curl -X POST https://ppromm.com/api/public/verify-receipt \
  -H "x-api-key: rv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "image_base64": "data:image/jpeg;base64,/9j/4AAQ...",
    "expected_amount": 25000,
    "currency": "MMK",
    "expected_receiver": "Aung Aung",
    "wallet": "kpay",
    "reference": "order_9182",
    "callback_url": "https://your-app.com/api/public/payment-webhook"
  }'
200 OK
{
  "submission_id": "0f3c9d1e-2b7a-4c55-9f21-8a1d4e6b7c90",
  "verdict": "approved",
  "confidence": 0.94,
  "reason": "Amount, receiver and transaction ID all matched.",
  "reason_codes": [],
  "extracted": {
    "wallet": "KBZPay",
    "amount": 25000,
    "currency": "MMK",
    "sender": "Mg Mg",
    "receiver": "Aung Aung",
    "receiver_account": "09xxxxxxxxx",
    "txn_id": "1234567890",
    "paid_at": "2026-08-16T12:04:00Z",
    "notes": null
  },
  "checks": [
    { "id": "amount", "label": "Amount matches", "passed": true },
    { "id": "receiver", "label": "Receiver matches", "passed": true },
    { "id": "duplicate", "label": "Transaction not reused", "passed": true }
  ],
  "ai_verdict": "genuine"
}

GET /api/public/verify-receipt/{id}

Polls a submission. Only submissions belonging to your API key are visible. Use it while a verdict is pending_review.

curl https://ppromm.com/api/public/verify-receipt/0f3c9d1e-2b7a-4c55-9f21-8a1d4e6b7c90 \
  -H "x-api-key: rv_live_xxx"

200 OK
{
  "submission_id": "0f3c9d1e-…",
  "reference": "order_9182",
  "verdict": "approved",
  "confidence": 0.72,
  "reason": "Approved by reviewer",
  "reason_codes": ["LOW_CONFIDENCE"],
  "extracted": { "…": "…" },
  "checks": [ "…" ],
  "reviewed_at": "2026-08-16T12:20:11Z",
  "created_at": "2026-08-16T12:05:02Z"
}

Recommended polling: every 10 seconds for up to 15 minutes, then fall back to the webhook.

Verdicts

approved
Receipt is genuine and matches the expected payment. Deliver the service.
pending_review
Uncertain. A PProMM reviewer decides; final verdict arrives by poll or webhook. Keep the order on hold.
rejected
Fake, tampered, unreadable, duplicate or mismatched. Ask the customer for a valid receipt.

Reason codes

AMOUNT_MISMATCHAmount does not match the expected payment
RECEIVER_MISMATCHReceiver account does not match the merchant account
DUPLICATE_TXNThis transaction ID was already submitted
IMAGE_UNREADABLEImage is blurry, cropped or unreadable
TAMPER_SUSPECTEDSigns of editing or tampering detected
NOT_A_RECEIPTImage is not a payment receipt
MISSING_TXN_IDNo transaction ID could be read
MISSING_AMOUNTNo amount could be read
TOO_OLDPayment time is outside the accepted window
LOW_CONFIDENCEAI confidence is too low for an automatic decision
AI_ERRORVerification service could not analyse the image

Webhooks

When a reviewer finalises a pending_review submission, PProMM POSTs to your callback URL.

POST https://your-app.com/api/public/payment-webhook
X-Receipt-Timestamp: 1755350411000
X-Receipt-Signature: <hex hmac-sha256>
Content-Type: application/json

{
  "event": "verification.reviewed",
  "submission_id": "0f3c9d1e-…",
  "reference": "order_9182",
  "verdict": "approved",
  "reviewed_at": "2026-08-16T12:20:11Z",
  "note": "Bank app screenshot confirmed",
  "extracted": { "…": "…" }
}

Verify the signature before trusting the payload: it is an HMAC-SHA256 of `${timestamp}.${rawBody}` using your webhook secret (whsec_…).

import { createHmac, timingSafeEqual } from "crypto";

const raw = await request.text();
const ts = request.headers.get("x-receipt-timestamp")!;
const sig = request.headers.get("x-receipt-signature")!;
const expected = createHmac("sha256", process.env.PAYMENTPRO_SECRET_KEY!)
  .update(`${ts}.${raw}`)
  .digest("hex");

if (!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
  return new Response("Invalid signature", { status: 401 });
}
const payload = JSON.parse(raw);

Respond with 2xx. Non-2xx responses are recorded as a failed delivery on the submission.

Errors

400Invalid JSON, failed validation, unreadable image, or image_url could not be fetched
401Invalid or inactive API key
404Submission not found for this API key
413Image is larger than 10MB
500Verification failed — safe to retry once

Duplicate protection is global: once a transaction ID is accepted it can never be reused, so your app does not need its own duplicate check.