Go-live checklist
هذا المحتوى غير متوفر بلغتك بعد.
Run this before an integration goes live. It proves the six things that break real integrations: the first load, an incremental run, a re-run that books nothing twice, a lost answer, a rejection and its re-send, and recovery after the cursor is lost.
Each step says what to do in the ERP extension and which call proves it. The calls are written
with curl so the same checks can be made by hand; your extension makes them itself.
SLFLO_BASE=https://api.slflo.com/api/integration/v1 # or your own copy's URLSLFLO_TOKEN=sfi_live_… # your sandbox tokenauth=(-H "Authorization: Bearer $SLFLO_TOKEN")These run in bash or zsh. -g stops curl from reading the brackets in filter[…] as a
glob.
0. Settings
Section titled “0. Settings”curl -s "${auth[@]}" "$SLFLO_BASE/me"Pass: data.company.code is the sandbox company (t-sandbox, or the code we sent with the
token), data.scopes holds every scope your jobs use, and data.client.token_prefix starts
sfi_live_ (sfi_test_ on a copy of Slflo you run yourself). Note rate_limit_per_minute and
set the extension’s throttle just under it.
curl -s "$SLFLO_BASE/openapi.json" | head -c 200 # public, no tokenPass: the document loads; your contracts match components.schemas (OrderDto, InvoiceDto,
PaymentDto, ReturnDto) — see the API reference.
The sandbox company is in ERP mode API, which order acks need (otherwise 409 ERP_MODE_NOT_API, see ERP mode). Its day’s two orders are
already delivered (an ack on them answers 409 ORDER_NOT_AWAITING_ACK), so step 1 acks its
invoices, payments and return; the first order ack comes with step 2’s new order.
1. First load
Section titled “1. First load”In the extension: clear staging, set every watermark to 0, run the pull job for orders, invoices, payments and returns, then the booking job.
v=0while :; do page=$(curl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[version_after]=$v&per_page=100") echo "$page" | jq -r '.data[] | "\(.id) \(.version) \(.number)"' [ "$(echo "$page" | jq .meta.has_more)" = true ] || break v=$(echo "$page" | jq .meta.next_version_after)donePass:
- Staging holds every order the loop printed, keyed
(id, version, line_number); the watermark equals the last page’snext_version_after. - The ERP has one sales order per Slflo order, with
CustomersOrderReference= the order’sid, customer taken fromcustomer.external_ref. - Every booked order was acked:
curl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[ack]=pending"returns only orders the extension deliberately did not book. - The same holds for
/invoices,/paymentsand/returns, each with its own watermark (or its ownfilter[seq_after]counter, starting fromGET /invoices/cursoretc.).
2. Incremental run
Section titled “2. Incremental run”Have a new pre-sale order placed on the sandbox (ask us; on your own copy, book one on the demo phone or in the console), then run the jobs again.
curl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[version_after]=$v" | jq '.data | map(.id)'Pass: only the new order (and any order changed since) comes back; one new sales order in the ERP; the watermark moves on.
3. Re-run: nothing booked twice
Section titled “3. Re-run: nothing booked twice”Run the pull and booking jobs a second time without any change in Slflo, then once more from an older watermark (e.g. half of the current one).
Pass:
- The re-read rows are rejected by the staging unique index or skipped as already staged.
- No second sales order, journal or credit note appears in the ERP.
- Repeating an ack with its original
Idempotency-Keyanswers the first answer again:
curl -si "${auth[@]}" -H "Idempotency-Key: SO-0012345" -H "Content-Type: application/json" \ -d '{"outcome":"ACCEPTED","external_ref":"SO-0012345","external_number":"SO-0012345"}' \ "$SLFLO_BASE/orders/<order id>/ack" | grep -i idempotent-replayed # Idempotent-Replayed: true4. Lost answer
Section titled “4. Lost answer”Simulate a timeout after the ERP created the sales order but before it stored or sent anything (kill the job, or drop the network, between “create SO” and “ack”).
Run the jobs again. Pass:
- Find-before-create finds the existing sales order by
CustomersOrderReferenceand does not create a second one. - The ack is sent (again) and lands once. Check from Slflo’s side:
curl -s "${auth[@]}" "$SLFLO_BASE/lookup?entity=order&id=<order id>"# → "external_ref": "SO-0012345", "exists": truecurl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[external_ref]=SO-0012345" | jq '.data | length' # 1Do the same for a master-data push: send PUT /products/ITEM-1001 twice with the same body,
each time with a new Idempotency-Key (under the same key the second call only replays the
first answer). Pass: the second answers "result": "unchanged" and no duplicate product exists
(GET /lookup?entity=product&external_ref=ITEM-1001 gives one id).
5. Rejection and re-send
Section titled “5. Rejection and re-send”Make one order fail in the ERP (a customer on hold, say), so the extension acks it:
curl -s "${auth[@]}" -H "Idempotency-Key: reject-<order id>" -H "Content-Type: application/json" \ -d '{"outcome":"REJECTED","reason_code":"CUSTOMER_ON_HOLD","message":"Customer CUST-0042 is on hold in D365"}' \ "$SLFLO_BASE/orders/<order id>/ack"Pass: the ack answers "status": "ERP_FAILED", and the office sees your message on the order
page. Fix the cause in the ERP, then have Send to ERP again pressed in the console (on the
hosted sandbox, ask us). Pass:
curl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[ack]=pending&filter[id]=<order id>" | jq '.data | length' # 1The next run books it and acks ACCEPTED; GET /lookup?entity=order&id=<order id> shows the SO
number.
6. Cursor lost, recovered by filter[ack]=pending
Section titled “6. Cursor lost, recovered by filter[ack]=pending”Simulate a restored ERP database: purge staging and set the order watermark back to 0 — or, to
be harsher, to the current high_water so the cursor skips everything.
Run the extension in its recovery mode (pull filter[ack]=pending instead of the cursor):
curl -sg "${auth[@]}" "$SLFLO_BASE/orders?filter[ack]=pending&per_page=500" | jq '.data | map(.id)'curl -sg "${auth[@]}" "$SLFLO_BASE/payments?filter[ack]=pending&per_page=500" | jq '.data | map(.id)'Pass: exactly the documents the ERP has not booked are returned; booking them creates no
duplicates (find-before-create); after the run every filter[ack]=pending list is empty, and
the normal cursor resumes from high_water.
Also: have a payment reversed or a cheque bounced after it was acked (on the hosted sandbox, ask
us). Pass: the payment
appears in filter[ack]=pending again with a reversal, and the extension reverses its
journal.
7. Master data out
Section titled “7. Master data out”curl -s "${auth[@]}" -X PUT -H "Idempotency-Key: ITEM-1001@7" -H "Content-Type: application/json" \ -d '{"name":{"en":"Cola 330ml"},"base_unit_ref":"PCS","active":true,"source_version":"7"}' "$SLFLO_BASE/products/ITEM-1001"curl -s "${auth[@]}" -X PUT -H "Idempotency-Key: ITEM-1001@6" -H "Content-Type: application/json" \ -d '{"name":{"en":"Cola"},"base_unit_ref":"PCS","active":true,"source_version":"6"}' "$SLFLO_BASE/products/ITEM-1001"PCS is the sandbox’s piece unit; keys are case-sensitive, so pcs would answer
422 UNKNOWN_REFERENCE until you push PUT /units/pcs.
Pass: the first is created (or updated/unchanged), the second stale and nothing
changes. A nightly batch through POST /batch/products with one bad item reports that item
ok: false with UNKNOWN_REFERENCE and applies the rest. A full re-run of the nightly job
answers unchanged for every item.
8. Failure policy
Section titled “8. Failure policy”- Make a call fail with
401while a job runs. On the hosted sandbox, don’t revoke the shared token: switch the job’s setting to a wrong token (401 TOKEN_INVALID); on your own copy you can revoke one in the console (401 TOKEN_REVOKED). Pass: the job stops and alerts, and it does not retry in a loop. - Exceed the rate limit (lower the throttle or run two jobs). Pass: on
429the client waitsRetry-Afterseconds and continues; nothing is marked failed. - A
500 INTERNAL(ask the Slflo team to simulate one) is retried with back-off and logged with itsrequest_id.
Sign-off
Section titled “Sign-off”| Step | Result | Notes / request ids |
|---|---|---|
| 0. Settings | ||
| 1. First load | ||
| 2. Incremental run | ||
| 3. Re-run | ||
| 4. Lost answer | ||
| 5. Rejection and re-send | ||
| 6. Cursor reset | ||
| 7. Master data out | ||
| 8. Failure policy |
When every row passes: the company’s administrator issues a token on the production company
(Settings → Integrations → New client), you ask us to set its ERP mode to API (see
ERP mode), put the token in the ERP settings, and schedule the
jobs.

