KYB documents
Owner/Admin members with a fresh MFA membership context can list and upload at /portal/merchants/{merchant}/kyb/documents, read private JPEG content at /{id}/content, and POST a versioned removal at /{id}/remove. Browser requests use the session/CSRF BFF. No public or presigned object URLs are issued. These are synthetic sandbox documents; attachment readiness is not KYB approval. In the hosted sandbox, upload made-up images only: never a real person’s or business’s identity document, registration or proof of address.
Upload JSON contains category, image_base64 and, for a BENEFICIAL_OWNER_ID document, an optional owner_id naming the draft’s owner it identifies (an id the saved draft does not hold returns 409 KYB_OWNER_UNKNOWN). Supported categories are RCCM, NATIONAL_ID, TAX_ID, DIRECTOR_ID, BENEFICIAL_OWNER_ID and ADDRESS_PROOF. The API accepts PNG/JPEG only: at most 1 MiB decoded input, 2048 pixels on each axis and 1 MiB after metadata-stripping JPEG conversion, re-encoded at a lower quality when needed. PDF, SVG, HTML, invalid images and unknown fields are rejected, with 400 DOCUMENT_TOO_LARGE, PNG_OR_JPEG_REQUIRED, DOCUMENT_DIMENSIONS or INVALID_DOCUMENT_IMAGE. The entire JSON request is capped at 1,450,000 bytes, including chunked bodies. The portal accepts source images up to 4 MiB, 6000 pixels per side and 24 megapixels, then reduces them before upload. Choose a smaller source if the resulting image remains too large. Original filenames are not sent or stored.
Upload requires Idempotency-Key and returns 202 PENDING after committing encrypted durable staging, metadata and the encrypted original response. A worker writes an authenticated-encrypted object to private SeaweedFS S3, a private Amazon S3 bucket in the hosted sandbox, before transitioning to READY. Storage failures retain PENDING staging and retry. Poll the list for the current state/version: an exact upload replay always returns the original PENDING response, even after storage succeeds. A changed body/category under the same key returns 409. Every replay checks current authorization.
Removal requires expected_version and Idempotency-Key. A new stale request conflicts; an exact successful replay returns its original response. REMOVED content returns 404 immediately. Pending removed files can still finish encrypted storage without becoming visible. The list shows kept documents only. The sandbox allows 24 kept documents and 24 MiB of sanitized content per merchant at once, and 200 uploads ever, removed ones included; beyond that an upload returns 409 DOCUMENT_LIMIT. This is a resource bound, not a legal retention policy. Removal does not erase retained evidence. The portal’s « Remplacer » uploads the new image first, then removes the one it replaces.
Metadata/audit contain opaque IDs, category, lifecycle and bounds; image data and replay fingerprints are encrypted with purpose/tenant/document or command binding. Private previews use no-store, nosniff responses and short-lived browser blob URLs revoked on close/context change. Local keys stay in ignored configuration outside PostgreSQL. Production malware scanning, authenticity checks, vault/rotation, retention/deletion and live activation remain release gates. Immutable submission is available separately. Submitted document evidence remains privately accessible after draft removal; compliance decisions follow.