Build your shipping workflow with Fayj.
Compose routes, create shipments and synchronise tracking with a REST API designed for commercial operations.
https://fayj.net/api/v1HTTPSBefore your first call
The customer deposits parcels at the selected freight agency. Fayj then composes the freight and European delivery legs. A destination routing rule is required before composing a route or requesting a quote.
Authentication
Create a key in the client portal under API. Keep the secret on your server only: never put it in a storefront, browser script or Git repository. Business requests accept a Bearer token; the portals use an HttpOnly session.
Authorization: Bearer YOUR_FAYJ_API_KEY Content-Type: application/json Idempotency-Key: order-24091-create
Idempotent mutations
For creations, updates and deletions marked idempotent, send an Idempotency-Key of 8–255 characters. Reuse it when retrying the same operation; use a new key for a different operation. Responses are retained for 24 hours and a replay may include Idempotency-Replayed: true.
Routing rules
Configure an active rule for each destination country in Shipments > Routing rules or through the API. It applies to manual shipments, imports and API calls. Use GET /routing/capabilities for available corridors and GET /network/transport-legs for active services.
{
"origin_country_code": "MA",
"origin_city": "Casablanca",
"destination_country_code": "FR",
"selection_mode": "AUTOMATIC",
"strategy": "RECOMMENDED",
"fallback": "NEXT_ELIGIBLE",
"enabled": true
}For a fixed route, use MANUAL and provide both freight_partner_id and delivery_service_leg_id. Automatic strategies are CHEAPEST, FASTEST and RECOMMENDED; STRICT or HOLD_FOR_REVIEW prevent a silent fallback.
{
"origin_country_code": "MA",
"origin_city": "Casablanca",
"destination_country_code": "FR",
"selection_mode": "MANUAL",
"freight_partner_id": "YOUR_FREIGHT_PARTNER_ID",
"delivery_service_leg_id": "YOUR_DELIVERY_LEG_ID",
"strategy": "CHEAPEST",
"fallback": "STRICT",
"enabled": true
}Compose a route
After saving a rule, compose the two legs. The engine filters capacity and requirements, then ranks options according to the rule. The recommended option is in meta.recommended_route_option_id. Use product: API for a website integration, or CONNECT for an import or portal workflow.
{
"product": "API",
"origin": { "country_code": "MA", "city": "Casablanca", "label": "Casablanca" },
"destination": { "country_code": "FR", "city": "Paris", "label": "Paris" },
"parcels": [{ "weight_kg": 5, "length_cm": 30, "width_cm": 20, "height_cm": 15 }]
}data[]Ranked eligible optionsmeta.recommended_route_option_idOption recommended by the rulemeta.shipment_overrideTrue if a one-off choice was appliedYou can choose a different pair for this composition only by sending freight_partner_id and delivery_service_leg_id together. The saved rule is unchanged.
Reserve a route
Select an option returned by composition. Fayj reserves its capacity for twenty minutes and returns a route_plan_id to use when creating the shipment. If the option expires or capacity changes, compose again.
{
"route_option_id": "ROUTE_OPTION_ID_FROM_COMPOSITION"
}Quotes
To display a price before shipment, use POST /quotes. The API applies the saved rule, chooses a compatible route and returns a quote with an amount in euro cents, an estimated date and contracting-provider information. An active rule is required here too.
curl -X POST https://fayj.net/api/v1/quotes \
-H "Authorization: Bearer YOUR_FAYJ_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quote-order-24091" \
-d '{
"origin_country": "MA",
"destination_country": "FR",
"parcel": { "weight_kg": 5, "length_cm": 30, "width_cm": 20, "height_cm": 15 }
}'The 201 response includes quote_id, amount, currency, estimated_delivery, contracting_party and legal_disclosure. Read the immutable snapshot with GET /quotes/{quote_id}. A quote and a route reservation are separate resources.
Shipments
Create a draft with the reserved route_plan_id, addresses, parcels, customs items and declarations. Use merchant_reference to reconcile the originating order. The recipient needs a first name or company; each customs item needs a quantity, value and country of origin.
{
"route_plan_id": "ROUTE_PLAN_ID_FROM_RESERVATION",
"merchant_reference": "ORDER-24091",
"sender": { "company": "Atlas Studio", "line1": "12 rue Atlas", "postal_code": "20000", "city": "Casablanca", "country_code": "MA" },
"recipient": { "first_name": "Claire", "last_name": "Martin", "line1": "18 rue Oberkampf", "postal_code": "75011", "city": "Paris", "country_code": "FR" },
"parcels": [{ "weight_kg": 5, "length_cm": 30, "width_cm": 20, "height_cm": 15 }],
"customs_items": [{ "description": "Cotton shirts for adults", "quantity": 2, "unit_value": 6000, "currency": "EUR", "country_of_origin": "MA", "reason": "SALE" }],
"incoterm": "DAP",
"declarations": { "accurate_information": true, "no_prohibited_goods": true, "regulated_goods_declared": true }
}unit_value and amount use minor currency units: 6000 represents EUR 60.00. Keep customs values at item level, with each item's currency and country of origin.
POST /shipments returns 201 with an FJ-XXXXXXXX identifier and DRAFT status. Only a draft can be edited with PATCH /shipments/{shipment_id}. Before confirmation, present the indicated contract versions and send them to POST /shipments/{shipment_id}/confirm with a new Idempotency-Key.
Customs documents
Attach an invoice or supporting document to a shipment with POST /shipments/{shipment_id}/documents. Send Base64 content in JSON with name, kind and content_type. Supported types are PDF, JPEG and PNG, up to 3 MB of decoded data per file.
{
"name": "commercial-invoice.pdf",
"kind": "CUSTOMS",
"content_type": "application/pdf",
"content_base64": "BASE64_ENCODED_PDF"
}GET /shipments/{shipment_id}/documents lists metadata; GET /shipments/{shipment_id}/documents/{document_id} downloads the content. Only the customer, Fayj and assigned partners can access these files.
Tracking
GET /shipments/{shipment_id}/tracking returns the shipment status and normalised events, retaining their source. GET /public/tracking/{shipment_id} can be used for a customer-facing link without exposing addresses or personal data.
{
"shipment_id": "FJ-12345678",
"status": "CONFIRMED",
"events": [{
"status": "CONFIRMED",
"description": "Order confirmed",
"occurred_at": "2026-09-15T09:20:00Z",
"source_type": "FAYJ"
}]
}Use the full FJ-XXXXXXXX identifier from the 201 response. A shipment owned by another organisation returns 404 rather than its details.
Webhooks
Register an HTTPS URL with POST /webhook-endpoints. The signing secret is returned once: store it server-side. Verify Fayj-Signature against the raw request bytes before processing a delivery, and deduplicate using Fayj-Delivery.
{
"url": "https://your-store.example/webhooks/fayj",
"events": ["shipment.confirmed", "shipment.cancelled"]
}Fayj-EventEvent typeFayj-DeliveryUnique delivery identifierFayj-TimestampUTC time in RFC 3339Fayj-Signaturev1=HMAC-SHA256Shipment events
shipment.confirmedShipment confirmedshipment.cancelledShipment cancelledshipment.tracking.updatedTracking event addedshipment.deliveredDelivery completedshipment.exceptionException to reviewThe signature is computed over timestamp + '.' + the raw JSON body using the whsec_… secret and SHA-256. Compare it in constant time. Return a 2xx status; other statuses and network failures are retried with increasing delays.
Errors & retries
Read the machine code in errors, not just the message. A 4xx error usually requires changing the request or configuration; a network error or 5xx may justify retrying with the same Idempotency-Key.
{
"error": {
"code": "ROUTING_PROFILE_REQUIRED",
"message": "Configure a routing rule for this destination before creating a shipment.",
"details": { "country_code": "FR" }
}
}401 · unauthorizedMissing or invalid key403 · forbiddenRole lacks permission409 · ROUTING_PROFILE_REQUIREDConfigure the destination409 · ROUTE_REVIEW_REQUIREDReview rule or service409 · route_option_expiredCompose again before reserving422 · NO_ROUTE_AVAILABLENo eligible routeSupport & claims
GET /shipments lists shipments visible to your organisation. A draft or confirmed shipment can be cancelled with POST /shipments/{shipment_id}/cancel while its state permits it. For an operational question, open a ticket with POST /support/tickets; provide shipment_id to address the freight partner on that route, or omit it to contact Fayj.
{
"shipment_id": "FJ-12345678",
"subject": "Customs document question",
"message": "Please confirm whether the invoice is complete."
}GET /support/tickets returns your conversations; POST /support/tickets/{ticket_id}/messages adds a reply. For a claim linked to a shipment, use POST /shipments/{shipment_id}/claims with reason and description, then GET /claims to follow accessible cases.
{
"reason": "DAMAGED_PARCEL",
"description": "The outer package arrived visibly damaged."
}Integrations
To connect a store, normalise each order into recipient, parcel and customs-item data. Configure destination countries in the Fayj portal before synchronising. Keep the order reference in merchant_reference; after confirmation, return the FJ-XXXXXXXX number and tracking to the store.
GET /routing-profilesCheck configured destinationsPOST /routes/composeApply the routing rulePOST /shipmentsCreate a compliant draftGET /shipments/{shipment_id}/trackingSynchronise trackingImport multiple orders
POST /connect/imports accepts a filename and up to 5,000 orders objects. Each row needs a unique reference, an address, weight and basic customs information. GET /connect/imports shows batch results and GET /connect/orders reconciles valid orders with any shipment created from them.
{
"filename": "orders-september.csv",
"orders": [{
"reference": "ORDER-24091",
"recipient_name": "Claire Martin",
"phone": "+33 600000000",
"address_line_1": "18 rue Oberkampf",
"postal_code": "75011",
"city": "Paris",
"country_code": "FR",
"weight_kg": 5,
"description": "Cotton shirts for adults",
"quantity": 2,
"unit_value": 6000,
"currency": "EUR"
}]
}Shopify
Store orders → Fayj APIIn your middleware, read fulfilment-ready orders, then map Shopify addresses and line items to Fayj fields. Send only orders whose destination country has a configured Fayj rule.
- Keep the Shopify order ID as merchant_reference
- Compose and reserve before creating the draft
- Write FJ-XXXXXXXX back to the order fulfilment
WooCommerce
Store REST API → Fayj APIRead eligible orders through the store API. Avoid duplicates with an Idempotency-Key derived from the order ID and operation, then store the Fayj identifier in order metadata.
- Choose the order status that triggers shipping
- Map addresses, parcels and customs products
- Synchronise tracking events back to the store
Magento
Commerce orders → Fayj APIMap Adobe Commerce orders, lines and addresses to Fayj shipment data. Preserve the order number in merchant_reference and confirm only after missing customs data has been completed.
- Check Commerce order-read permissions
- Provide origin country and value per item
- Store the Fayj tracking number on the order
Etsy
Shop orders → Fayj APIExport or retrieve Etsy shop orders in your integration layer. Verify addresses, weights and item values before calling Fayj; then attach tracking to the original order.
- Match the Etsy order reference
- Complete required customs declarations
- Return the FJ-XXXXXXXX number to the buyer
