{
  "openapi": "3.1.0",
  "info": {
    "title": "Payment Pro Myanmar (PProMM) Receipt Verification API",
    "version": "1.0.0",
    "description": "Verify mobile wallet / mobile banking payment receipts (KPay, KBZPay, Wave, AYA Pay, CB Pay, MMQR) with AI. Each submission returns one of three verdicts: approved, pending_review or rejected with machine readable reason codes.",
    "contact": {
      "name": "PProMM",
      "url": "https://ppromm.com"
    }
  },
  "servers": [
    {
      "url": "https://ppromm.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Verification",
      "description": "Submit and poll receipt verifications"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/public/verify-receipt": {
      "post": {
        "tags": [
          "Verification"
        ],
        "operationId": "verifyReceipt",
        "summary": "Submit a payment receipt for verification",
        "description": "Provide the receipt image as base64 (data URL or raw) or as a fetchable URL. Max decoded image size is 10MB. The response is returned synchronously; `pending_review` submissions are finalised later by a human reviewer and delivered to `callback_url` as a signed webhook.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body, failed validation or unreadable image",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Image is larger than 10MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "Verification"
        ],
        "operationId": "verifyReceiptPreflight",
        "summary": "CORS preflight",
        "security": [],
        "responses": {
          "204": {
            "description": "No content"
          }
        }
      }
    },
    "/api/public/verify-receipt/{id}": {
      "get": {
        "tags": [
          "Verification"
        ],
        "operationId": "getVerification",
        "summary": "Get the current status of a submission",
        "description": "Poll this endpoint for submissions returned as `pending_review`. Only submissions belonging to the authenticated client app are visible.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The `submission_id` returned by POST /api/public/verify-receipt",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Submission status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmissionStatus"
                }
              }
            }
          },
          "400": {
            "description": "Invalid submission id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Submission not found for this client app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "Verification"
        ],
        "operationId": "getVerificationPreflight",
        "summary": "CORS preflight",
        "security": [],
        "responses": {
          "204": {
            "description": "No content"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Client app API key issued in the PProMM dashboard (format: `rv_...`)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "required": [
          "error"
        ]
      },
      "Verdict": {
        "type": "string",
        "enum": [
          "approved",
          "pending_review",
          "rejected"
        ],
        "description": "approved: Approved | pending_review: Pending review | rejected: Rejected"
      },
      "ReasonCode": {
        "type": "string",
        "enum": [
          "AMOUNT_MISMATCH",
          "RECEIVER_MISMATCH",
          "DUPLICATE_TXN",
          "IMAGE_UNREADABLE",
          "TAMPER_SUSPECTED",
          "NOT_A_RECEIPT",
          "MISSING_TXN_ID",
          "MISSING_AMOUNT",
          "TOO_OLD",
          "LOW_CONFIDENCE",
          "AI_ERROR"
        ],
        "description": "AMOUNT_MISMATCH: Amount does not match the expected payment | RECEIVER_MISMATCH: Receiver account does not match the merchant account | DUPLICATE_TXN: This transaction ID was already submitted | IMAGE_UNREADABLE: Image is blurry, cropped or unreadable | TAMPER_SUSPECTED: Signs of editing or tampering detected | NOT_A_RECEIPT: Image is not a payment receipt | MISSING_TXN_ID: No transaction ID could be read | MISSING_AMOUNT: No amount could be read | TOO_OLD: Payment time is outside the accepted window | LOW_CONFIDENCE: AI confidence is too low for an automatic decision | AI_ERROR: Verification service could not analyse the image"
      },
      "VerifyRequest": {
        "type": "object",
        "description": "Either `image_base64` or `image_url` is required.",
        "properties": {
          "image_base64": {
            "type": "string",
            "minLength": 32,
            "maxLength": 15000000,
            "description": "Receipt image as a data URL (`data:image/png;base64,...`) or raw base64."
          },
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Publicly fetchable image URL."
          },
          "reference": {
            "type": "string",
            "maxLength": 200,
            "description": "Your order / invoice reference."
          },
          "expected_amount": {
            "type": "number",
            "minimum": 0,
            "maximum": 1000000000
          },
          "currency": {
            "type": "string",
            "minLength": 2,
            "maxLength": 8,
            "default": "MMK"
          },
          "expected_receiver": {
            "type": "string",
            "maxLength": 200,
            "description": "Merchant phone number or account name."
          },
          "wallet": {
            "type": "string",
            "maxLength": 60,
            "description": "Optional wallet hint, e.g. KPay, WavePay."
          },
          "callback_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Webhook URL for the reviewed result. Falls back to the client app callback URL."
          }
        },
        "anyOf": [
          {
            "required": [
              "image_base64"
            ]
          },
          {
            "required": [
              "image_url"
            ]
          }
        ],
        "examples": [
          {
            "image_base64": "data:image/png;base64,iVBORw0KGgo...",
            "reference": "order-123",
            "expected_amount": 15000,
            "currency": "MMK",
            "expected_receiver": "09xxxxxxxxx",
            "callback_url": "https://your-app.example.com/api/public/payment-pro-webhook"
          }
        ]
      },
      "ExtractedReceipt": {
        "type": "object",
        "properties": {
          "wallet": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "sender": {
            "type": [
              "string",
              "null"
            ]
          },
          "receiver": {
            "type": [
              "string",
              "null"
            ]
          },
          "receiver_account": {
            "type": [
              "string",
              "null"
            ]
          },
          "txn_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp when readable."
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CheckResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "passed": {
            "type": "boolean"
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "passed"
        ]
      },
      "VerificationResult": {
        "type": "object",
        "properties": {
          "submission_id": {
            "type": "string",
            "format": "uuid"
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "reason": {
            "type": "string"
          },
          "reason_codes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReasonCode"
            }
          },
          "extracted": {
            "$ref": "#/components/schemas/ExtractedReceipt"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckResult"
            }
          },
          "ai_verdict": {
            "type": "string"
          }
        },
        "required": [
          "submission_id",
          "verdict",
          "confidence",
          "reason",
          "reason_codes"
        ]
      },
      "SubmissionStatus": {
        "type": "object",
        "properties": {
          "submission_id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "confidence": {
            "type": "number"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason_codes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReasonCode"
            }
          },
          "extracted": {
            "$ref": "#/components/schemas/ExtractedReceipt"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckResult"
            }
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "submission_id",
          "verdict",
          "created_at"
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "description": "Sent to `callback_url` after a human review. Signed with HMAC-SHA256 of the raw body in the `X-Receipt-Signature` header using your webhook secret (`whsec_...`).",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "verification.reviewed"
            ]
          },
          "submission_id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "extracted": {
            "$ref": "#/components/schemas/ExtractedReceipt"
          }
        },
        "required": [
          "event",
          "submission_id",
          "verdict"
        ]
      }
    }
  }
}