Fayj API

Build your shipping workflow with Fayj.

Compose routes, create shipments and synchronise tracking with a REST API designed for commercial operations.

Base URLhttps://fayj.net/api/v1HTTPS
ConfigureComposeReserveShip

Before 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.

EURAmounts in euro cents
20 minRoute reservation
FJ-XXXXXXXXTracking identifier
01

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.

Request headers
Authorization: Bearer YOUR_FAYJ_API_KEY
Content-Type: application/json
Idempotency-Key: order-24091-create
A new key's secret is returned only once. LIVE keys can only be created when production booking is enabled.

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.

02

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.

POST /routing-profiles · automatic
{
  "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.

POST /routing-profiles · manual
{
  "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
}
A destination without an active rule returns 409 ROUTING_PROFILE_REQUIRED. The configure_url in the error points to client setup.
03

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.

POST /routes/compose
{
  "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 options
meta.recommended_route_option_idOption recommended by the rule
meta.shipment_overrideTrue if a one-off choice was applied

You 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.

04

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.

POST /route-plans
{
  "route_option_id": "ROUTE_OPTION_ID_FROM_COMPOSITION"
}
A route_plan_id can be attached to only one shipment. The shipment countries and parcels must match the reserved route.
05

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.

POST /quotes
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.

06

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.

POST /shipments
{
  "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.

A shipment containing regulated goods remains subject to review. DDP is accepted only when enabled; DAP is the default.
07

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.

POST /shipments/{shipment_id}/documents
{
  "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.

08

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.

GET /shipments/{shipment_id}/tracking
{
  "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.

09

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.

POST /webhook-endpoints
{
  "url": "https://your-store.example/webhooks/fayj",
  "events": ["shipment.confirmed", "shipment.cancelled"]
}
Fayj-EventEvent type
Fayj-DeliveryUnique delivery identifier
Fayj-TimestampUTC time in RFC 3339
Fayj-Signaturev1=HMAC-SHA256

Shipment events

shipment.confirmedShipment confirmed
shipment.cancelledShipment cancelled
shipment.tracking.updatedTracking event added
shipment.deliveredDelivery completed
shipment.exceptionException to review

The 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.

Asynchronous webhook delivery requires the worker and PostgreSQL. Without them, do not rely on webhooks alone: also use GET /shipments/{shipment_id}/tracking.
10

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 response
{
  "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 key
403 · forbiddenRole lacks permission
409 · ROUTING_PROFILE_REQUIREDConfigure the destination
409 · ROUTE_REVIEW_REQUIREDReview rule or service
409 · route_option_expiredCompose again before reserving
422 · NO_ROUTE_AVAILABLENo eligible route
Route composition also returns INVALID_ROUTE_OVERRIDE if only one of the two manual service identifiers is supplied.
11

Support & 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.

POST /support/tickets
{
  "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.

POST /shipments/{shipment_id}/claims
{
  "reason": "DAMAGED_PARCEL",
  "description": "The outer package arrived visibly damaged."
}
12

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 destinations
POST /routes/composeApply the routing rule
POST /shipmentsCreate a compliant draft
GET /shipments/{shipment_id}/trackingSynchronise tracking

Import 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.

POST /connect/imports
{
  "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"
  }]
}
The response separates valid_rows and invalid_rows. Invalid rows do not necessarily prevent valid rows being retained; inspect errors before creating shipments.
Explore all integrations

Shopify

Store orders → Fayj API

In 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.

  1. Keep the Shopify order ID as merchant_reference
  2. Compose and reserve before creating the draft
  3. Write FJ-XXXXXXXX back to the order fulfilment

WooCommerce

Store REST API → Fayj API

Read 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.

  1. Choose the order status that triggers shipping
  2. Map addresses, parcels and customs products
  3. Synchronise tracking events back to the store

Magento

Commerce orders → Fayj API

Map 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.

  1. Check Commerce order-read permissions
  2. Provide origin country and value per item
  3. Store the Fayj tracking number on the order

Etsy

Shop orders → Fayj API

Export 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.

  1. Match the Etsy order reference
  2. Complete required customs declarations
  3. Return the FJ-XXXXXXXX number to the buyer