Documents API
Generate trade documents from shipment data. Documents are generated asynchronously — the endpoint returns job IDs that you poll for completion.
Document generation is asynchronous. The endpoint returns 202 Accepted with job IDs. Poll the jobs table or use webhooks to track completion.
Generate Documents
POST /functions/v1/generate-documents
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
shipment | object | Yes | The full shipment data object |
document_types | string[] | Yes | Array of document type keys to generate |
shipment_id | string | No | Shipment UUID. When provided, enables regeneration tracking with tier-based generation limits. |
final_doc | boolean | No | When true, all fields render as static text. When false or omitted, empty fields become fillable PDF form fields. Default: false. |
confirm_warnings | boolean | No | Set to true to bypass soft warnings (missing optional fields). Default: false. |
validation_hash | string | No | SHA-256 hash from a prior validation run. Embedded in document footers for verification. |
previous_shipment_data | object | No | Previous shipment data for diff computation on regeneration. Only used when shipment_id is provided. |
Example Request
curl -X POST "https://<project-ref>.supabase.co/functions/v1/generate-documents" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_ANON_KEY" \
-d '{
"shipment": {
"exporter": { "name": "Acme Corp" },
"importer": { "name": "Global Trade GmbH" },
"route": { "origin_country": "US", "destination_country": "DE" },
"goods": { "currency": "USD", "total_value": 25000, "line_items": [] }
},
"document_types": ["COMMERCIAL_INVOICE", "PACKING_LIST"],
"validation_hash": "a1b2c3d4..."
}'
Available Document Types (16)
Core Trade Documents
| Type | Description |
|---|---|
COMMERCIAL_INVOICE | Commercial invoice |
PACKING_LIST | Packing list |
BILL_OF_LADING | Bill of lading |
CERTIFICATE_OF_ORIGIN | Certificate of origin |
PROFORMA_INVOICE | Proforma invoice |
DANGEROUS_GOODS_DECLARATION | Dangerous goods declaration (DGD) |
AIR_WAYBILL | Air waybill |
Logistics & Financial
| Type | Description |
|---|---|
SHIPPERS_LETTER_OF_INSTRUCTION | Shipper's letter of instruction |
VGM_DECLARATION | Verified gross mass declaration (SOLAS) |
INSURANCE_CERTIFICATE | Marine cargo insurance certificate |
Preferential Trade Agreements
| Type | Description |
|---|---|
EUR1_MOVEMENT_CERTIFICATE | EUR.1 movement certificate (EU FTA partners) |
USMCA_CERTIFICATE_OF_ORIGIN | USMCA certificate of origin (US/CA/MX) |
STATEMENT_ON_ORIGIN | Statement on origin (EU FTA) |
Commodity-Specific Certificates
| Type | Description |
|---|---|
PHYTOSANITARY_CERTIFICATE | Phytosanitary certificate (plant products) |
HEALTH_VETERINARY_CERTIFICATE | Health / veterinary certificate (animal products) |
CERTIFICATE_OF_ANALYSIS | Certificate of analysis (lab-tested products) |
Response (Success — 202)
{
"jobs": [
{
"jobId": "uuid-1234",
"docType": "COMMERCIAL_INVOICE",
"rynkoJobId": "rynko-job-5678"
},
{
"jobId": "uuid-5678",
"docType": "PACKING_LIST",
"rynkoJobId": "rynko-job-9012"
}
],
"generation_count": 2,
"max_generations": 10,
"generation_history_id": "uuid-hist-1234"
}
The generation_count, max_generations, and generation_history_id fields are only present when shipment_id is provided.
Response (Pre-Check Errors — 400)
Hard errors that block generation. All errors across all requested document types are collected and returned at once.
{
"success": false,
"preCheckErrors": [
{
"docType": "DANGEROUS_GOODS_DECLARATION",
"errors": [
{
"message": "No line items flagged as hazardous",
"fieldPath": "goods.lineItems.0.complianceFlags",
"tab": "goods"
},
{
"message": "Emergency contact required for DGD",
"fieldPath": "hazardousShipment.emergencyContact",
"tab": "hazardous"
}
]
}
]
}
Response (Soft Warnings — 200)
Warnings for missing optional fields. The document can still be generated — the fields will be blank. Re-submit with confirm_warnings: true to proceed.
{
"success": false,
"requiresConfirmation": true,
"warnings": [
{
"docType": "BILL_OF_LADING",
"warnings": [
{
"message": "Freight payment terms not set (PREPAID/COLLECT)",
"fieldPath": "blData.freightPayment",
"tab": "logistics"
},
{
"message": "Place of delivery not set",
"fieldPath": "blData.placeOfDelivery",
"tab": "logistics"
}
]
}
]
}
Pre-Check Validation Rules
The following document types have mandatory pre-checks that block generation if conditions are not met.
DANGEROUS_GOODS_DECLARATION
- At least one line item must be flagged as hazardous (
compliance_flagsincludesdgdoris_hazardous: true) - Each hazardous line item must have
un_numberandhazard_class hazardous_shipment.emergency_contactis required
HEALTH_VETERINARY_CERTIFICATE
veterinary_data.competent_authorityis requiredveterinary_data.establishment_numberis required
PHYTOSANITARY_CERTIFICATE
phytosanitary_data.issuing_authorityis required
CERTIFICATE_OF_ANALYSIS
analysis_data.batch_numberis requiredanalysis_data.test_standardis required
EUR1_MOVEMENT_CERTIFICATE
- Destination country must be an EU FTA partner (EU member states plus CH, NO, IS, LI, TR)
- Origin country must not be US (no EU preferential trade agreement)
STATEMENT_ON_ORIGIN
- Same rules as EUR.1 Movement Certificate
USMCA_CERTIFICATE_OF_ORIGIN
- Destination country must be US, CA, or MX
Soft Warning Rules
The following document types produce soft warnings for missing optional fields. These do not block generation but indicate that fields will be blank on the generated document.
| Document Type | Warning Fields |
|---|---|
BILL_OF_LADING | bl_data.freight_payment, bl_data.place_of_delivery |
INSURANCE_CERTIFICATE | insurance_data.insurance_company, insurance_data.policy_number, insurance_data.coverage_type, insurance_data.claims_agent |
VGM_DECLARATION | vgm_data.weighing_method, vgm_data.authorised_person, vgm_data.weighing_station |
SHIPPERS_LETTER_OF_INSTRUCTION | sli_data.forwarding_agent_name, sli_data.eei_filing |
AIR_WAYBILL | awb_data.issuing_carrier, awb_data.agent_name |
Regeneration
When shipment_id is provided, the endpoint tracks generation attempts and enforces tier-based limits. Each call atomically increments the shipment's generation_count. If the limit is exceeded, the endpoint returns 429.
A generation_history record is created for each regeneration, including:
- A snapshot of the shipment data at generation time
- A diff against
previous_shipment_data(if provided) - The list of document types generated
- The validation hash used
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | KLV_VAL_002 | shipment and document_types are required |
| 404 | KLV_SHIP_004 | Shipment not found (when shipment_id is provided) |
| 409 | KLV_GEN_LIMIT | Concurrent generation detected — retry |
| 429 | KLV_GEN_LIMIT | Generation limit exceeded for this shipment. Upgrade your plan. |
Polling Strategy
Poll the Supabase jobs table every 2 seconds for each job ID:
async function waitForDocument(jobId) {
for (let i = 0; i < 30; i++) {
const { data: job } = await supabase
.from('jobs')
.select('id, status, rynko_job_id, error_message')
.eq('id', jobId)
.single();
if (job.status === 'completed') return job;
if (job.status === 'failed') throw new Error(job.error_message);
await new Promise(r => setTimeout(r, 2000));
}
throw new Error('Generation timed out');
}