/meds/{medId}/file/upload
Background
/meds/{medId}/file/upload is the MED evidence file upload endpoint for partners. Partners can upload analysis files (evidence files) for a specific MED, such as transaction proof or communication records. Each file must be classified under an evidenceType in the MED's Evidence Requirements. Uploaded files are securely stored and associated with the MED for subsequent analysis.
Files can only be uploaded while the MED is in the WAITING or EVIDENCE_REQUIRED status; multiple files can be uploaded for a single MED while it remains in either status.
Endpoint
| Item | Value |
|---|---|
| Method | POST |
| Path | /meds/{medId}/file/upload |
| Content-Type | multipart/form-data |
| Purpose | Upload analysis evidence files for a MED |
Authentication
Use ES256 request signing and include all five required headers: X-Merchant-Id, X-Timestamp, X-Nonce, Digest, and Authorization. Set X-Merchant-Id to the partner's top-level merchant number and keyId to the key version, v1 by default. The signature is a DER-encoded ECDSA signature encoded with standard Base64. See Request Signing.
Generate the timestamp, nonce, digest, and signature placeholders for each actual request. The canonical string includes the actual path and raw query; sign again when pagination or filter parameters change.
Request Fields
| Field | Location | Type | Required | Description |
|---|---|---|---|---|
medId | Path | string | Yes | Unique identifier of the MED infraction report (the platform case number, medc prefix + digits), maximum length 64 characters. |
evidenceType | Body | string | Yes | Evidence type; must be a valid type in the MED's current evidence checklist. |
file | Body | file | Yes | Analysis evidence file; supports PDF, DOC, DOCX, TXT, JPG, JPEG, and PNG formats, up to 10 MB. For security reasons, the file extension is validated against the content type and the file content. |
Request Examples
The Digest is calculated over the complete request body bytes that are actually sent, and the server recalculates it on receipt. Every stage must therefore use the same bytes: build the complete request body file first, calculate the digest and sign over all of its bytes, then send it unchanged. Any stage that changes the bytes causes signature verification to fail.
Step 1: Build the .multipart request body file. The boundary is fixed as MED_UPLOAD_BOUNDARY and must not be generated randomly (the boundary in the Content-Type header must match the one inside the file). The PDF example contains evidenceType=MERCHANT_ORDER_RECORD and file (evidence-document.pdf, application/pdf); the image example contains evidenceType=LOGIN_IP_DEVICE_RECORD and file (screenshot-proof.png, image/png). The file must contain all form fields, part headers, the original file bytes, and CRLF separators. Its structure is shown below (\r\n stands for the two bytes carriage return + line feed; the file content is embedded as-is, not Base64):
--MED_UPLOAD_BOUNDARY\r\n
Content-Disposition: form-data; name="evidenceType"\r\n
\r\n
MERCHANT_ORDER_RECORD\r\n
--MED_UPLOAD_BOUNDARY\r\n
Content-Disposition: form-data; name="file"; filename="evidence-document.pdf"\r\n
Content-Type: application/pdf\r\n
\r\n
<original bytes of evidence-document.pdf>\r\n
--MED_UPLOAD_BOUNDARY--\r\nExamples that build the file and calculate the Digest (for the image example, replace the evidenceType, filename, and Content-Type):
// Go 1.16+, standard library only (use the ioutil package on older versions).
package main
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
)
const boundary = "MED_UPLOAD_BOUNDARY"
func main() {
fileBytes, err := os.ReadFile("evidence-document.pdf")
if err != nil {
panic(err)
}
var body bytes.Buffer
body.WriteString("--" + boundary + "\r\n")
body.WriteString("Content-Disposition: form-data; name=\"evidenceType\"\r\n\r\n")
body.WriteString("MERCHANT_ORDER_RECORD\r\n")
body.WriteString("--" + boundary + "\r\n")
body.WriteString("Content-Disposition: form-data; name=\"file\"; filename=\"evidence-document.pdf\"\r\n")
body.WriteString("Content-Type: application/pdf\r\n\r\n")
body.Write(fileBytes)
body.WriteString("\r\n--" + boundary + "--\r\n")
if err := os.WriteFile("evidence-document.multipart", body.Bytes(), 0o644); err != nil {
panic(err)
}
// Digest over all bytes of the complete body, not over the PDF file alone
sum := sha256.Sum256(body.Bytes())
fmt.Println("SHA-256=" + base64.StdEncoding.EncodeToString(sum[:]))
}// Java 8+, standard library only.
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.Base64;
public class BuildMultipartBody {
public static void main(String[] args) throws Exception {
String boundary = "MED_UPLOAD_BOUNDARY";
byte[] fileBytes = Files.readAllBytes(Paths.get("evidence-document.pdf"));
StringBuilder head = new StringBuilder();
head.append("--").append(boundary).append("\r\n");
head.append("Content-Disposition: form-data; name=\"evidenceType\"\r\n\r\n");
head.append("MERCHANT_ORDER_RECORD\r\n");
head.append("--").append(boundary).append("\r\n");
head.append("Content-Disposition: form-data; name=\"file\"; filename=\"evidence-document.pdf\"\r\n");
head.append("Content-Type: application/pdf\r\n\r\n");
byte[] headBytes = head.toString().getBytes(StandardCharsets.UTF_8);
byte[] tailBytes = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);
byte[] body = new byte[headBytes.length + fileBytes.length + tailBytes.length];
System.arraycopy(headBytes, 0, body, 0, headBytes.length);
System.arraycopy(fileBytes, 0, body, headBytes.length, fileBytes.length);
System.arraycopy(tailBytes, 0, body, headBytes.length + fileBytes.length, tailBytes.length);
Files.write(Paths.get("evidence-document.multipart"), body);
// Digest over all bytes of the complete body, not over the PDF file alone
String digest = "SHA-256=" + Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-256").digest(body));
System.out.println(digest);
}
}// Node.js 14+, built-in modules only.
import crypto from "node:crypto";
import fs from "node:fs";
const boundary = "MED_UPLOAD_BOUNDARY";
const fileBytes = fs.readFileSync("evidence-document.pdf");
const body = Buffer.concat([
Buffer.from(
`--${boundary}\r\n` +
'Content-Disposition: form-data; name="evidenceType"\r\n\r\n' +
"MERCHANT_ORDER_RECORD\r\n" +
`--${boundary}\r\n` +
'Content-Disposition: form-data; name="file"; filename="evidence-document.pdf"\r\n' +
"Content-Type: application/pdf\r\n\r\n"
),
fileBytes,
Buffer.from(`\r\n--${boundary}--\r\n`),
]);
fs.writeFileSync("evidence-document.multipart", body);
// Digest over all bytes of the complete body, not over the PDF file alone
const digest = "SHA-256=" + crypto.createHash("sha256").update(body).digest("base64");
console.log(digest);<?php
// PHP 7.1+, standard extensions only.
$boundary = 'MED_UPLOAD_BOUNDARY';
$fileBytes = file_get_contents('evidence-document.pdf');
$body =
"--{$boundary}\r\n" .
'Content-Disposition: form-data; name="evidenceType"' . "\r\n\r\n" .
"MERCHANT_ORDER_RECORD\r\n" .
"--{$boundary}\r\n" .
'Content-Disposition: form-data; name="file"; filename="evidence-document.pdf"' . "\r\n" .
"Content-Type: application/pdf\r\n\r\n" .
$fileBytes .
"\r\n--{$boundary}--\r\n";
file_put_contents('evidence-document.multipart', $body);
// Digest over all bytes of the complete body, not over the PDF file alone
echo 'SHA-256=' . base64_encode(hash('sha256', $body, true)), PHP_EOL;# Python 3.7+, standard library only.
import base64
import hashlib
from pathlib import Path
boundary = "MED_UPLOAD_BOUNDARY"
file_bytes = Path("evidence-document.pdf").read_bytes()
body = (
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="evidenceType"\r\n\r\n'
"MERCHANT_ORDER_RECORD\r\n"
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="file"; filename="evidence-document.pdf"\r\n'
"Content-Type: application/pdf\r\n\r\n"
).encode() + file_bytes + f"\r\n--{boundary}--\r\n".encode()
Path("evidence-document.multipart").write_bytes(body)
# Digest over all bytes of the complete body, not over the PDF file alone
digest = "SHA-256=" + base64.b64encode(hashlib.sha256(body).digest()).decode()Step 2: Generate the signature. Using the Digest from the previous step, build the canonical string and sign it following Request Signing (the (request-target) line uses the actual request path /meds/{medId}/file/upload) to produce the Authorization header.
Step 3: Send it unchanged. Send the .multipart file byte-for-byte with --data-binary, as shown below (in Python this means passing body directly as the request data; do not use files= or any parameter that rebuilds the request body).
Two common mistakes both lead to signature verification failure:
- Hashing only the uploaded file itself — the server recalculates the digest over the complete multipart body it receives, so the two digests never match.
- Regenerating the boundary or request body with
--form(or withfiles=and similar multipart auto-construction) after signing — the client generates a new random boundary and reorders fields on its own, so the bytes sent differ from the bytes signed.
PDF file example:
curl --request POST \
--url '<base_url>/meds/medc2874510938274639021/file/upload' \
--header 'X-Merchant-Id: <MERCHANT_ID>' \
--header 'X-Timestamp: <UNIX_TIMESTAMP_SECONDS>' \
--header 'X-Nonce: <UNIQUE_NONCE>' \
--header 'Digest: SHA-256=<MULTIPART_BODY_SHA256_BASE64>' \
--header 'Authorization: Signature keyId="v1",alg="ES256",headers="(request-target) x-timestamp x-nonce digest",signature="<ES256_SIGNATURE_BASE64>"' \
--header 'Content-Type: multipart/form-data; boundary=MED_UPLOAD_BOUNDARY' \
--data-binary '@evidence-document.multipart'Image file example:
curl --request POST \
--url '<base_url>/meds/medc2874510938274639021/file/upload' \
--header 'X-Merchant-Id: <MERCHANT_ID>' \
--header 'X-Timestamp: <UNIX_TIMESTAMP_SECONDS>' \
--header 'X-Nonce: <UNIQUE_NONCE>' \
--header 'Digest: SHA-256=<MULTIPART_BODY_SHA256_BASE64>' \
--header 'Authorization: Signature keyId="v1",alg="ES256",headers="(request-target) x-timestamp x-nonce digest",signature="<ES256_SIGNATURE_BASE64>"' \
--header 'Content-Type: multipart/form-data; boundary=MED_UPLOAD_BOUNDARY' \
--data-binary '@screenshot-proof.multipart'Response Fields
The endpoint uses the standard status, msg, and data response envelope. Uploaded file information is returned in data.
| Field | Type | Always Returned | Description |
|---|---|---|---|
status | int | Yes | Response code |
msg | string | Yes | Corresponds to status |
data | object | On success | Uploaded file information; may be omitted when the request fails at signature verification or protocol parsing. |
data.id | string | Yes | Unique identifier of the uploaded file. |
data.medId | string | Yes | The MED ID the file is associated with. |
data.subMerchantNo | string | Yes | Sub-merchant number that owns the MED. |
data.evidenceType | string | Yes | Evidence type the file is classified under. |
data.createdAt | string | Yes | File upload time, ISO 8601 UTC time string (yyyy-MM-ddTHH:mm:ssZ). |
Response Example
{
"status": 200,
"msg": "sucesso",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"medId": "medc2874510938274639021",
"subMerchantNo": "24922653000123",
"evidenceType": "MERCHANT_ORDER_RECORD",
"createdAt": "2026-06-02T09:10:00Z"
}
}Response Error Codes
Business Rules
File Upload Requirements
- The maximum file size is 10 MB.
- Supported formats: PDF, DOC, DOCX, TXT, JPG, JPEG, and PNG.
- For security reasons, the file extension is validated against the content type.
- Each upload specifies only one
evidenceType; multiple files can be uploaded under the same evidence type. evidenceTypemust come fromdata.requirements[].evidenceTypereturned by Get Evidence Requirements.- The caller must have access to the account associated with the MED.
MED Status Validation
- Files can only be uploaded while the MED is in the
WAITINGorEVIDENCE_REQUIREDstatus. - Files cannot be uploaded while the MED is in any other status; after the platform returns the submission (
EVIDENCE_REQUIRED), additional files can be uploaded. - While an analysis has been submitted and is pending platform review (
UNDER_REVIEW), no more files can be uploaded. - After the evidence submission deadline (
dueTime) has passed, files cannot be uploaded. - Multiple files can be uploaded for a single MED while it remains in the
WAITINGorEVIDENCE_REQUIREDstatus.
Back to MED API Overview