Create a cheque directly — POST /api/cheque-log/external
One endpoint records an externally printed cheque from your system with no screen and no preview. Its fields with literal rules, the guards that raise 422, the 201 response, and what to know about duplicates and images.
Last updated: 2026-10-24
On this page
When to use it
When your system prints the cheque itself (or does not print at all) and you want a row in the cheque log with its initial status for follow-up in the app. No preview, no printing — to print from the app’s screen use the Dynamics flow.
Route: POST /api/cheque-log/external — alias POST /api/cheque-logs (audited under the first name).
Fields
| Field | Literal rule | Required | Default |
|---|---|---|---|
bank_name |
required|string|max:255 |
✅ | — |
template_name |
nullable|string|max:255 |
null | |
cheque_type |
IN or OUT |
null ⟹ no status | |
cheque_date |
nullable|date + year between 2000 and 2100 |
null | |
stub_date |
nullable|date |
null | |
recipient_name · place · signed_by · reason · reference_number · stub_name · stub_reason |
nullable|string|max:255 |
null | |
amount_text · note · liner_text |
nullable|string |
null | |
amount |
nullable|numeric — ≤ 13 integer digits, fraction per currency |
null | |
stub_amount |
nullable|numeric — ≤ 8 digits, decimals within the currency's scale |
null | |
currency |
nullable|string|max:10 |
EGP |
|
cheque_number |
nullable|string|max:50 + unique per bank among printed |
null | |
company_id |
nullable|integer|exists:companies,id |
the key’s company — if sent and different ⟹ 422 | |
bank_account_id · cheque_book_id · partner_id |
nullable|integer|exists |
null | |
cheque_leaf_id |
nullable|integer|exists + the leaf guard (below) |
— | |
liner_enabled · two_lines |
boolean |
false | |
image |
nullable|image|max:200048 (KB) |
— | |
image_base64 |
nullable|string |
— | |
save_cheque_image |
nullable|boolean |
false | |
external_source · external_id |
nullable|string |
null — the optional idempotency key |
Contract notes:
cheque_status_idis silently ignored — the status derives fromcheque_type:OUT⟹ being issued ·IN⟹ in wallet · unknown ⟹ no status (which you don’t want — always send the direction).bank_idis accepted but not read here — the bank resolves frombank_namealone by a case-insensitive match after trimming; send the name as/api/banksreturns it.- The image is not stored unless
save_cheque_image: truewithimage_base64/image— and without a watermark. cheque_leaf_id: an available leaf of an issued-cheque book in the key's company; the cheque number, book and account come from it, and it is marked used with the row.
Guards that raise 422
| Case | Message (Arabic) |
|---|---|
Number belongs to a cheque-book leaf and no cheque_leaf_id is sent (issued cheques only: a leaf in a book of the account sent, or of the bank's books in the company when no account is sent) |
هذا الرقم يخص دفتر شيكات — اطبعه من وضع الدفتر |
cheque_leaf_id of a deleted or missing leaf |
الورقة المختارة غير موجودة. (leaf not found) |
cheque_leaf_id on a received cheque (IN) |
أوراق الدفاتر للشيكات الصادرة وحدها. (book leaves are for issued cheques only) |
The leaf does not belong to the cheque_book_id, bank_account_id or bank_name sent |
الورقة المختارة لا تتبع الدفتر/الحساب/البنك المرسَل. (leaf does not match the book/account/bank sent) |
| The leaf is not available (reserved · used · damaged) | الورقة المختارة غير متاحة — حالتها: {status}. (leaf not available — its status) |
cheque_number sent and different from the leaf's number |
رقم الشيك لا يطابق رقم الورقة المختارة ({n}). (number does not match the leaf) |
| The leaf's book is not active | الدفتر غير نشط. (book not active) |
| The leaf's number is printed on another cheque of the same bank | رقم الشيك {n} موجود بالفعل (مطبوع) على نفس البنك. (number already printed on this bank) |
| Number reserved in an open batch | الرقم :number محجوز في دفعة مفتوحة (الدفعة #:batch) — اطبعها أو ألغِها أولًا. |
| Duplicate in its context — same company, direction and bank (plus the account for issued · the customer for received, when sent) | رقم الشيك {N} مستعمَل بالفعل على بنك {البنك} — اختر رقمًا آخر |
| Amount too large | القيمة كبيرة جدًا — لا يزيد الجزء الصحيح عن 13 خانة. |
| Fraction digits | كسر المبلغ يتجاوز خانات {العملة} (خانتان عشريتان). |
| Cheque date year before 2000 or after 2100 | تاريخ الشيك يجب أن يقع بين سنة 2000 وسنة 2100. (cheque date must fall between 2000 and 2100) |
| Reference from another company | المستفيد لا يتبع شركة الشيك. · الحساب البنكي المختار لا يتبع شركة الشيك. · دفتر الشيكات المختار … · الورقة المختارة لا تتبع شركة الشيك. |
| Key without a company | مفتاح الـAPI غير منسوب لشركة — عيّن شركته من شاشة «التكامل ← مفاتيح API». |
company_id contradicts the key |
الحمولة تطلب شركة «…» بينما مفتاح الـAPI منسوب لشركة «…» — احذف الحقل أو استعمل مفتاح الشركة الصحيحة. |
Example
curl -s -X POST "APP_URL/api/cheque-log/external" \
-H "X-API-KEY: KEY" -H "Content-Type: application/json" -H "Accept: application/json" \
-d '{
"bank_name": "بنك مصر",
"template_name": "بنك مصر - نموذج 1",
"cheque_type": "OUT",
"recipient_name": "شركة النيل للتوريدات",
"amount": 1250.50,
"currency": "EGP",
"cheque_date": "2026-10-15",
"cheque_number": "100200",
"reference_number": "PV-1001",
"reason": "دفعة مورّد",
"signed_by": "خالد عبد الرحمن"
}'
201:
{
"success": true,
"message": "تم الحفظ بنجاح",
"data": { "id": 4321, "bank_name": "بنك مصر", "cheque_number": "100200", "amount": "1250.500", "…": "the full cheque_logs row" },
"image_saved": false
}
data is the full row (100+ columns, including lifecycle columns that are empty now) — rely on id, cheque_number, bank_name, amount, cheque_date, currency, and do not build on the rest.
Errors
| Status | Body |
|---|---|
| 422 | {"message":"Validation failed","errors":{"cheque_number":["…"]}} — the field is cheque_number, cheque_leaf_id or cheque_book_id depending on the guard; messages in Arabic |
| 500 | {"message":"Error saving cheque log","error":"…"} |
| 503 · 401 · 403 · 429 | from the key ladder |
Stamped server-side
printed_at = now() · printed_by = null · initial status from the direction · and company_id = the key’s company, always. The payload is checked against it: a different company_id, or a reference (account · book · leaf · partner) from another company ⟹ 422. No fallback and no default company on the key path.
With cheque_leaf_id: the leaf is locked and checked inside the same save transaction, and the cheque number, book and account come from it (send cheque_number equal to its number, or omit it); it becomes used with the row. On the key path the leaf is not restricted to the book's next leaf — the call records a cheque that was already printed. A resend with the same external_source + external_id returns the existing row (200 reused) and leaves the leaf as it is.
The row is confirmed — and idempotency
The row is created printed and confirmed (the paper left outside the app): its number cannot be edited afterwards, like any confirmed cheque, and it enters confirmed reports and reminders — so send the number right the first time.
Three duplicate guards (since 1.8.0):
- Cheque number in its context: a second call with the same number and bank in the same company and direction (and the same account for issued cheques when sent · the same customer for received ones when
partner_idis sent) ⟹ 422 “already used” — treat it as a prior success in your logic. - External key (optional): send
external_source+external_id(as in Dynamics) — a resend returns the existing row with 200 and"reused": true, no new row (Dynamics usesaction: "reused"instead). - A row stamped for another company, or a final row (damaged · cancelled · reissued) ⟹ 422 instead of an update.
Recommendation: send both cheque_number and external_id.
Related
Did this page answer your question?
Thanks — your feedback helps us improve the guide.
Couldn't send — please try again shortly.