Skip to main content

Signing Seller API Requests (multipart/form-data)

The Create Seller and Update Seller APIs send data as multipart/form-data (because they include a file upload in the files field), unlike most other Baokim APIs which send application/json. Because of this, how you compute the Signature for these two APIs differs from the general guidance on the API Introduction page — this page covers that difference.

Before continuing, make sure you're familiar with 6. Signature Format Details and 7. General Security Guidelines on the Introduction page — the rules about keyVersion, algorithm, the RSA/ED25519 key pair, and the Signature header still apply as-is. Only the content being signed (plaintext) is different.

1. Difference from regular JSON APIs​

Regular JSON APICreate/Update Seller API
Content-Typeapplication/jsonmultipart/form-data
Signed data (plaintext)The raw JSON request body, verbatimA JSON object rebuilt from all parsed form fields (see section 2)

Baokim determines whether a request is "multipart" based on the Content-Type header. If this header contains multipart/form-data or application/x-www-form-urlencoded, the system does not use the raw body to verify the signature (the raw body of a multipart request contains boundaries, per-part headers, and binary file data — it can't be signed/compared directly like JSON). Instead, the system rebuilds a JSON object from all the parsed fields, and verifies against that.

2. How to build the plaintext to sign​

plaintext = JSON of every field in the form (including the files field), encoded with the JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE flags.

plaintext = json_encode(<all parsed form fields>, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)

Important notes when rebuilding this object on your side:

  1. Regular text/array fields (request_id, request_time, merchant_code, seller_id, platform, ...): include the exact value you sent, keeping the same data type (string/array) as documented in the Request table of each API.

  2. The files field — this is the easiest part to get wrong: the binary content of the file is not included in the plaintext. On the server side, each file element in the files array is an internal file-handle object with no public properties exposed — so when the whole payload is json_encode()-ed, every file element always serializes to an empty object {}, regardless of the actual file name or content. When you rebuild the plaintext to sign on your side, replace the value of each file key with {} (an empty object) — not the file name, not the file content, not a base64 encoding of the file.

    Example (2 files elements with different type):

    "files": [
    { "type": "1", "file": {} },
    { "type": "2", "file": {} }
    ]
  3. Field order: add fields to the object in the exact order they were sent in the multipart form (usually the order your HTTP client/SDK builds the request). Since json_encode() preserves the key order of the source array, a different order produces a different plaintext → signature verification will fail.

3. Full example​

Using the sample request from the Create Seller page, the plaintext to sign is:

{
"request_id": "MERCHANT12345678901234510281",
"request_time": "2024-11-15 14:36:00",
"merchant_code": "MERCHANT001",
"sub_merchant_code": "MERCHANT001",
"seller_id": "SELLER123456",
"seller_name": "Nguyễn Văn A",
"platform": [
{
"type": "2",
"links": "https://shopee.vn/seller123",
"platform_seller_id": "SHOPEE123456"
}
],
"files": [
{ "type": "1", "file": {} },
{ "type": "2", "file": {} }
],
"representative_DOB": "1990-01-01",
"representative_ID_number": "001099012345",
"representative_name": "Nguyen Van A",
"representative_nationality": "Vietnam",
"representative_address": "1 Dai Co Viet Street, Hai Ba Trung District, Ha Noi",
"seller_business_type": "2",
"seller_business_registration_no": "0123456789",
"seller_phone": "0912345678",
"seller_email": "[email protected]",
"seller_business_description": "Online retail store selling electronics",
"seller_risk_level": "1",
"gc_code": "5045",
"mcc_code": "5399"
}

Once you have this plaintext, the remaining steps (signing with your private key using the registered algorithm, base64-encoding the signature, attaching it to the Signature header along with keyVersion/algorithm) follow the general guidance in section 6 of the Introduction page.

4. Common mistakes​

SymptomCommon cause
Signature always fails even though the data sent is correctReal file content/name was put into the plaintext instead of {}
Signature only fails when there's more than 1 fileThe order of elements in the files array doesn't match the order they were sent
Signature works in a pure-JSON test but fails on the real multipart submissionSigning the raw body (application/json style) was used for a multipart/form-data request