Skip to content

Acks

The cursor answers what changed. Acks answer what haven’t you taken yet — which survives a lost cursor, a restored database or a purged staging table.

Rejecting an order the ERP can't book

POST /orders/ord_01J9X/ack

Terminal window
curl -X POST 'https://api.slflo.com/api/integration/v1/orders/ord_01J9X/ack' \
-H 'Authorization: Bearer $SLFLO_TOKEN' \
-H 'Idempotency-Key: SO-0012345' \
-H 'Content-Type: application/json' \
-d '{"outcome":"REJECTED","reason_code":"CUSTOMER_ON_HOLD","message":"Customer CUST-0042 is on hold in D365"}'

Response

HTTP 200
{
"data": {
"id": "ord_01J9X",
"number": "SO-000123",
"status": "ERP_FAILED",
"version": 18343,
"result": "applied",
"ack": {
"outcome": "REJECTED",
"reason_code": "CUSTOMER_ON_HOLD",
"message": "Customer CUST-0042 is on hold in D365",
"external_ref": null,
"external_number": null,
"acked_version": 18343,
"acked_at": "2026-10-05T10:42:00Z"
}
}
}
  • Order ACCEPTED → the order moves to ERP_CONFIRMED and remembers your key: it appears as the order’s external_ref, and filter[external_ref] and GET /lookup find it by it.
  • Order REJECTED → the order moves to ERP_FAILED. The office sees your message on the order and can send it again after fixing the cause (unblocking the customer, say). That makes it pending again, and your next pull sees it.
  • The ack is stored at a version — ack.acked_version in the answer. An order ack moves the order, so it is the order’s new version.
  • Invoices, payments, returns: POST /invoices/{id}/ack, /payments/{id}/ack, /returns/{id}/ack take the same body. The ack is kept at the version it answered: a document that changes after its ack — a reversed payment, a bounced cheque — is pending again until you ack its new version. That’s how you learn about it.

POST /acks answers up to 500 documents of one kind — entity is order, invoice, payment or return — in one request:

{"entity": "payment",
"items": [{"id": "pay_01J9", "outcome": "ACCEPTED", "external_ref": "JRN-000881"}]}

Each item gets its own result — applied, unchanged, or an error — and one bad item never fails the rest.

Situation Answer
The same ack again unchanged
A different answer for an order you already answered 409 ALREADY_ACKNOWLEDGED (unless the office sent it again since)
Your key already used for another record 409 EXTERNAL_REF_CONFLICT
An order not waiting for the ERP 409 ORDER_NOT_AWAITING_ACK
The company isn’t in ERP mode API (orders only) 409 ERP_MODE_NOT_API — see ERP mode

You may follow the cursor (stream style) or only filter[ack]=pending (queue style). The reference connector uses the cursor plus acks, and falls back to pending after a cursor reset.

Ack the sandbox’s first invoice with a journal number of yours, then send the same call again:

Terminal window
id=$(curl -sg "$SLFLO_BASE/invoices?filter[seq_after]=0&per_page=1" -H "Authorization: Bearer $SLFLO_TOKEN" | jq -r '.data[0].id')
for try in 1 2; do
curl -s -X POST "$SLFLO_BASE/invoices/$id/ack" -H "Authorization: Bearer $SLFLO_TOKEN" \
-H "Content-Type: application/json" -H "Idempotency-Key: ack-$id-$try" \
-d '{"outcome":"ACCEPTED","external_ref":"JRN-000001","external_number":"JRN-000001"}' | jq -c '.data | {result, acked_version: .ack.acked_version}'
done
# {"result":"applied","acked_version":…} then {"result":"unchanged","acked_version":…}

On the shared sandbox someone may have acked it already: then you see unchanged (the same answer) or 409 ALREADY_ACKNOWLEDGED (a different one), as the table above says.