Skip to main content

How to Integrate

This guide ties together the POS Session, Payment Method, Pricelist, and Order endpoints into a single, practical walkthrough for building a POS integration against API v1, including a full end-to-end payload, how to query the resulting data, and how to handle errors.

Prerequisites​

  • A valid API key — see the Authentication Guide.
  • All requests are made to https://{account-name}.juleb.com/api/v1.
  • All examples below send Authorization: ****** — replace it with your real API key.

Choose your integration mode​

POST /pos/order supports two modes, controlled by the autoCreateSession field:

ModeWhen to use itWhat you send
AutoYou're integrating an external/RSD-style system that only needs to push completed sales into Juleb and should not have to manage POS sessions, configs, or cashiers itself.company_id + autoCreateSession: true (+ optional autoSessionConfig)
ManualYou're building a client that manages the full ERP POS operation yourself — opening/closing sessions, picking a cashier config, choosing payment methods, etc.An explicit pos_session_id + payment_method_id you already obtained

Both modes create the same kind of order and support the same line formats described below — the only difference is who's responsible for resolving the session/config/payment method.


Manual flow: session → order → read back​

Use this flow when your integration owns the full POS lifecycle.

1. Create a session​

POST /pos/order

is preceded by opening a session for a known POS config:

POST /pos/session
{
"configId": 12
}

This returns the new pos_session_id. See Create a POS Session for details.

2. (Optional) Look up payment methods and pricelists​

GET /pos/payment-method?fields=id,name
GET /pos/pricelist?fields=id,name

Use these to resolve the payment_method_id / pricelist_id you want to use for the order if your integration doesn't already have them cached. See Get Many Payment Methods and Get Many Pricelists.

3. Create the order​

POST /pos/order
{
"pos_session_id": 1799,
"payment_method_id": 5,
"pricelist_id": 1,
"partner_id": 42,
"pos_reference": "EXT-ORDER-10293",
"lines": [
{
"product_id": 91,
"qty": 2,
"discount_by_percent": 10
}
]
}

See Post POS Order for the full field reference. The response contains the new order's id and pos_reference.

4. Read the order back​

GET /pos/order/{id}
GET /pos/order/{id}/line
GET /pos/order/{id}/payment

See Get One Order, Get Many Lines, and Get Many Payments.

5. Close the session​

Once you're done processing orders for that session (e.g. end of shift):

POST /pos/session/{id}

See Close and POST Session. A session should normally stay open while you're actively sending orders to it — avoid closing and reopening a session per order.


Auto flow: let the API manage the session​

Use this flow for RSD-style or other external integrations that just need to push a completed sale, without tracking sessions/configs themselves.

POST /pos/order
{
"company_id": 3,
"autoCreateSession": true,
"autoSessionConfig": {
"name": "rsd-integration",
"allowOutOfStock": true
},
"pos_reference": "EXT-ORDER-10293",
"lines": [
{
"product_id": 91,
"qty": 2,
"discount_by_percent": 10
}
]
}

What happens behind the scenes:

  1. The API looks for a POS config for company_id named rsd-integration (from autoSessionConfig), creating one if it doesn't exist yet.
  2. It reuses an already opened session for that config, or opens a new one — you never need to call /pos/session yourself.
  3. payment_method_id and pricelist_id are resolved automatically for that company if you didn't provide them.

Because the session is reused across calls, repeated requests with the same company_id / autoSessionConfig.name land on the same open session — there's no need (and no endpoint call required) to close it between orders. Omit autoSessionConfig entirely to just use/create the company's default config.

company_id is required in auto mode

autoCreateSession: true without company_id returns a 400 Bad Request.


Building order lines​

There are two independent ways to describe lines[], selected with selectProductByGtin:

A. Manual lines (default)​

Provide product_id + qty yourself:

{
"product_id": 91,
"qty": 1,
"lot_name": "923190297493",
"discount_by_percent": 0,
"price_unit": 45
}

You can mix in a qr_code array on a manual line purely to record scanned serial numbers for traceability — it has no effect on which product or quantity is used:

{
"product_id": 91,
"qty": 2,
"qr_code": [
"010628602300100021557558631092319029749317270915",
"010628602300100021557558631092319029749317270916"
]
}

B. GTIN/QR-resolved lines (selectProductByGtin: true)​

Set selectProductByGtin: true at the top level of the request, and provide only qr_code per line — no product_id/qty:

{
"selectProductByGtin": true,
"lines": [
{ "qr_code": ["010628602300100021557558631092319029749317270915"] },
{ "qr_code": ["010628602300100021557558631092319029749317270916"] }
]
}

Each code is parsed for its GTIN and batch/lot number; the matching product is looked up automatically, and codes sharing the same batch + GTIN are grouped into a single line with qty equal to the number of codes scanned for that batch.

Don't mix modes in the same request

When selectProductByGtin is true, every line must provide qr_code — a line with only product_id/qty and no qr_code will fail with no Qr codes provided. If you need both manually-priced lines and GTIN-scanned lines in the same order, send manual lines (mode A) and attach qr_code to them for traceability instead of using selectProductByGtin.


Full end-to-end payload example​

A combined example using the auto flow, a partner, a pricelist override, a per-line discount, and a scanned serial number for traceability:

const axios = require('axios');
const accountName = process.env.ACCOUNT_NAME;
const apiKey = process.env.API_KEY;

axios.post(`https://${accountName}.juleb.com/api/v1/pos/order`, {
company_id: 3,
autoCreateSession: true,
autoSessionConfig: {
name: 'rsd-integration',
allowOutOfStock: true
},
pricelist_id: 1,
partner_id: 42,
pos_reference: 'EXT-ORDER-10293',
lines: [
{
product_id: 91,
qty: 2,
lot_name: '923190297493',
discount_by_percent: 10,
qr_code: ['010628602300100021557558631092319029749317270915']
}
]
}, {
headers: {
'accept': '*/*',
'Authorization': `******`,
'Content-Type': 'application/json'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error);
});

Response​

[
{
"id": 7155,
"pos_reference": "EXT-ORDER-10293"
}
]

Querying the data afterward​

Every GET list endpoint (orders, lines, payments, sessions, configs, payment methods, pricelists) supports the same fields / filter / page / limit query parameters — see Mastering API Queries for the full syntax.

Common examples for orders created through this guide:

# Orders created today above a given total
GET /pos/order?fields=name,date_order,amount_total,session_id&filter=date_order>2024-01-01&page=1&limit=50

# Lines for a specific order, returns only, excluding sales
GET /pos/order/7155/line?fields=name,product_id,qty,price_subtotal_incl,is_return&filter=is_return=false

# Payments recorded for a specific order
GET /pos/order/7155/payment?fields=name,payment_method_id,amount

Use the pos_reference you sent on creation (or the returned id) as the correlation key between your system and Juleb when querying orders back.


Error handling​

StatusMessageCause
400pos_session_id and payment_method_id are required if autoCreateSession is falseManual mode was used without supplying both pos_session_id and payment_method_id (and no autoCreateSession/company_id).
400company_id is required if autoCreateSession is trueAuto mode was requested without company_id.
400Order lines cannot be emptylines was missing or an empty array.
400invalid pos_session_id: {id}The pos_session_id provided does not exist or isn't linked to a valid POS config.
400Batch/Lot {lot_name} not foundlot_name doesn't match an existing batch and the session's config doesn't allow selling out of stock.
400no gtin found for product {qrCode} / no batch found for qr code {qrCode} / no product found for GTIN {gtin}A qr_code couldn't be parsed or resolved to a known product while selectProductByGtin was true.
400no Qr codes providedselectProductByGtin was true but a line had no qr_code.
401UnauthorizedMissing or invalid Authorization header.
404Record not foundThe id (order/line/payment) requested on a GET endpoint doesn't exist, or doesn't belong to the given parent order.