curl --request POST \
--url https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/ \
--header 'Content-Type: multipart/form-data' \
--header 'X-API-Version: <api-key>' \
--header 'X-Client-ID: <api-key>' \
--header 'X-Client-Secret: <api-key>' \
--header 'X-Merchant-ID: <api-key>' \
--form file=@example-file \
--form type=offer_letterimport requests
url = "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "type": "offer_letter" }
headers = {
"X-Client-ID": "<api-key>",
"X-Client-Secret": "<api-key>",
"X-Merchant-ID": "<api-key>",
"X-API-Version": "<api-key>"
}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('type', 'offer_letter');
const options = {
method: 'POST',
headers: {
'X-Client-ID': '<api-key>',
'X-Client-Secret': '<api-key>',
'X-Merchant-ID': '<api-key>',
'X-API-Version': '<api-key>'
}
};
options.body = form;
fetch('https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-API-Version: <api-key>",
"X-Client-ID: <api-key>",
"X-Client-Secret: <api-key>",
"X-Merchant-ID: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Client-ID", "<api-key>")
req.Header.Add("X-Client-Secret", "<api-key>")
req.Header.Add("X-Merchant-ID", "<api-key>")
req.Header.Add("X-API-Version", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/")
.header("X-Client-ID", "<api-key>")
.header("X-Client-Secret", "<api-key>")
.header("X-Merchant-ID", "<api-key>")
.header("X-API-Version", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Client-ID"] = '<api-key>'
request["X-Client-Secret"] = '<api-key>'
request["X-Merchant-ID"] = '<api-key>'
request["X-API-Version"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Document uploaded",
"data": {
"ref": "8207383264",
"type": "offer_letter"
}
}Upload Document
Upload a supporting file and get back a ref to send under its checklist code. Multipart file + type.
curl --request POST \
--url https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/ \
--header 'Content-Type: multipart/form-data' \
--header 'X-API-Version: <api-key>' \
--header 'X-Client-ID: <api-key>' \
--header 'X-Client-Secret: <api-key>' \
--header 'X-Merchant-ID: <api-key>' \
--form file=@example-file \
--form type=offer_letterimport requests
url = "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "type": "offer_letter" }
headers = {
"X-Client-ID": "<api-key>",
"X-Client-Secret": "<api-key>",
"X-Merchant-ID": "<api-key>",
"X-API-Version": "<api-key>"
}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('type', 'offer_letter');
const options = {
method: 'POST',
headers: {
'X-Client-ID': '<api-key>',
'X-Client-Secret': '<api-key>',
'X-Merchant-ID': '<api-key>',
'X-API-Version': '<api-key>'
}
};
options.body = form;
fetch('https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-API-Version: <api-key>",
"X-Client-ID: <api-key>",
"X-Client-Secret: <api-key>",
"X-Merchant-ID: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Client-ID", "<api-key>")
req.Header.Add("X-Client-Secret", "<api-key>")
req.Header.Add("X-Merchant-ID", "<api-key>")
req.Header.Add("X-API-Version", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/")
.header("X-Client-ID", "<api-key>")
.header("X-Client-Secret", "<api-key>")
.header("X-Merchant-ID", "<api-key>")
.header("X-API-Version", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-pacb-uat.eximpe.com/pg/payments/{payment_id}/documents/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Client-ID"] = '<api-key>'
request["X-Client-Secret"] = '<api-key>'
request["X-Merchant-ID"] = '<api-key>'
request["X-API-Version"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\noffer_letter\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Document uploaded",
"data": {
"ref": "8207383264",
"type": "offer_letter"
}
}Overview
Uploads one supporting file for a payment’s verification and answers with aref. You then send that ref under the document’s checklist code when you submit verification details:
{ "documents": [{ "code": "offer_letter", "ref": "8207383264" }] }
ref is what binds a file to a checklist item, and it does that later, on the submit.
ref under a code. That separation is what lets a payer send a passport on Monday and a letter of admission on Thursday without you tracking a half-built request.The request
multipart/form-data, two parts:
| Part | |
|---|---|
file | The document itself. |
type | What kind of document it is — one of the codes below. |
Document types
type must be one of these. They are the codes the checklists ask for, so the value you upload under is the same value you submit under:
type | |
|---|---|
offer_letter | Letter of admission from the institution |
passport | Student’s passport |
student_id_or_visa | Student visa or university ID card — one code, either document |
student_id | University ID card (S1107, which has no travel leg) |
fee_invoice | Fee invoice |
passenger_list | Name and passport number of every traveller |
traveller_passports | Passports of all travellers |
traveller_visas | Visas of all travellers |
ticket | Travel tickets |
invoice | Commercial invoice, or the invoice for a trip |
relationship_declaration | Self-declaration of the remitter’s relationship to the person the remittance is for |
The file
Validated on upload, so a bad file fails here rather than at submit time.| Extensions | pdf · png · jpg · jpeg · docx · xlsx · csv |
| Maximum size | 10 MB |
| Encrypted PDFs | Rejected — a password-protected file cannot be read by anyone who has to review it |
The response
201, with the ref:
{
"success": true,
"message": "Document uploaded",
"data": {
"ref": "8207383264",
"type": "offer_letter"
}
}
documents[].ref on the verification submit.The stored file is scoped to the merchant the payment settles to — the sub-merchant named on the order, not the credential that uploaded it — and not to the payment in the path. So a ref stays valid across a retried attempt or a recurring installment, and is citable on any payment settling to that same merchant, or to that merchant’s parent or one of its children. A ref outside that scope reads as unknown rather than as someone else’s file.A ref uploaded against one sub-merchant’s payment is not citable on a sibling’s. The scope follows the settling account, so a file two sub-merchants both need is uploaded once for each.A ref owned by the PSP account itself is the exception: it resolves on any payment settling to that PSP or to any of its sub-merchants.type you sent.Replacing a document
Uploads are immutable. To replace one, upload the file again — you get a newref — and submit that ref under the same code. The newer row supersedes the older without discarding it.
Waiving instead of uploading
Some documents may be discharged with a reason instead of a file. Those carry"waivable": true in the requirements; for them you skip this endpoint entirely and send a waiver_reason on the submit:
{ "documents": [{ "code": "traveller_visas", "waiver_reason": "Destination is visa-free for this passport" }] }
400. See Submit Verification Details.
POST /pg/lrs/documents/ predates this route and still works as an alias over the same implementation. New integrations should use the payment-scoped path.Related
Get Verification Requirements
Submit Verification Details
ref is bound to a checklist code.Authorizations
Client Application ID - Your unique application identifier used to authenticate API requests. You can find your Client ID in the Developer Settings section of the merchant dashboard.
Client Secret Key - Your secret key used alongside the Client ID for secure authentication. Keep this confidential and never expose it in client-side code. Available in the Developer Settings section of the merchant dashboard.
Merchant Identifier - The unique ID for the merchant account. This is required for PSP (Payment Service Provider) merchants who manage multiple merchant accounts. You can find merchant IDs in the Merchant Management section of the dashboard.
API Version - Specifies which version of the API to use (e.g., '1.X.X', '2.X.X', or '3.X.X'). This header allows you to control which API version your integration uses. Default version information is available in the Developer Settings.
Path Parameters
UID of the payment the document is for.
Body
The document file. Allowed extensions: pdf, png, jpg, jpeg, docx, xlsx, csv. Maximum 10 MB. Password-protected PDFs are rejected.
Document type — one of the codes the checklists ask for, so the value you upload under is the value you submit under. Read the ones this payment actually needs from Get Verification Requirements rather than mapping this list into your code: it is the complete set any checklist can ask for, and it grows as the regulatory matrix is revised. delivery_proof is the one type here that no checklist asks for: it is a post-settlement compliance document, uploaded through this endpoint and then filed by ref through Submit post-settlement documents.
offer_letter, passport, student_id_or_visa, student_id, fee_invoice, passenger_list, traveller_passports, traveller_visas, ticket, invoice, relationship_declaration, delivery_proof "offer_letter"