Bank of Abyssinia is having issues right now.Status →
Siinqee Bank unavailable (8 days). Known, ongoing.Status →
links.et

Zemen Bank

Detail page: URL token structure (24-char prefix + reference composite), PDF parsing quirks, and full return type.

Zemen's share.zemenbank.com serves a PDF directly (with a download attachment header). We grab the bytes, parse with pdf-parse, and extract structured fields.

URL format

https://share.zemenbank.com/<prefix>/<token>/pdf
PartPatternExample
hostliteralshare.zemenbank.com
<prefix>2 lettersrt (retail / ATM) · ft (funds transfer) · others observed in the wild
<token>24 chars [A-Za-z0-9]00000000ABCDEFGHIJKLMNOP

Token anatomy

The 24-char token is <8-char prefix><16-char reference>. The trailing 16 chars are the actual reference number that appears in the receipt body under "Reference No". The leading 8 chars look like a branch / timestamp code that's only used at the URL level.

00000000ABCDEFGHIJKLMNOP
└──────┬─┘└──────┬─────┘
 8-char        16-char
 URL only      reference (echoed in receipt)

The resolver accepts any 2-letter prefix, so when Zemen mints a new code (cr, dr, etc.) you don't need a redeploy on our side.

Return type

receipt.source === "zemen-pdf". Amounts are numbers; currency is always "ETB" on these receipts.

interface ZemenReceipt {
	source: 'zemen-pdf';
 
	// Header
	invoiceNo?: string;
	date?: string; // e.g. "3-Jun-2026"
 
	// Counterparties
	payerName?: string;
	payerAccount?: string; // e.g. "173****0000(923141XXXXXX0000)"
	payerTin?: string; // empty unless payer is VAT-registered
	payerVat?: string;
	recipientName?: string;
	recipientAccount?: string; // masked
 
	// Transaction
	paymentOrderNumber?: string;
	reference?: string; // last 16 chars of the URL token
	transactionStatus?: string; // "COMPLETED" | …
	transactionDetail?: string; // e.g. "Remot On Us - ATM CASH WITHDRAWAL"
 
	// Amounts (numeric)
	settledAmount?: number;
	serviceCharge?: number;
	vat?: number; // 15%
	disasterRiskCharge?: number; // 5%
	totalAmountPaid?: number;
	currency?: 'ETB'; // always ETB on these receipts
 
	// Misc
	totalAmountInWord?: string; // e.g. "ONE HUNDRED ONE BIRR AND TWENTY CENT(S)"
	paymentReason?: string; // free text, often empty
}

Sample (placeholder values)

{
	"source": "zemen-pdf",
	"invoiceNo": "000000000",
	"date": "1-Jan-2026",
	"payerName": "<payer name>",
	"payerAccount": "173****0000(923141XXXXXX0000)",
	"payerTin": "",
	"payerVat": "",
	"recipientName": "<recipient name>",
	"recipientAccount": "00****00",
	"paymentOrderNumber": "",
	"reference": "ABCDEFGHIJKLMNOP",
	"transactionStatus": "COMPLETED",
	"transactionDetail": "Remot On Us - ATM CASH WITHDRAWAL",
	"settledAmount": 100,
	"serviceCharge": 1,
	"vat": 0.15,
	"disasterRiskCharge": 0.05,
	"totalAmountPaid": 101.2,
	"currency": "ETB",
	"totalAmountInWord": "ONE HUNDRED ONE BIRR AND TWENTY CENT(S)",
	"paymentReason": ""
}

Quirks

  • Date field uses the Amharic colon: the top-of-document date label ends in ፡ (U+1361), not ASCII :. Our regex accepts both.
  • No whitespace between table cells: pdf-parse strips spacing such that Service ChargeETB 1.00 arrives as one token. Amount-row regex uses [ \t]* between the label and ETB, not \s+.
  • Empty-value rows collapse onto the next label: when a field like "Payer TIN" is unset, pdf-parse emits the label followed immediately by a newline. Our label regex uses [ \t]* for the trailing whitespace so we don't slurp the next label's value into the current field.
  • Some receipts return HTTP 404 application/json when the reference is no longer available upstream (Zemen cycles them out of cache). We detect non-PDF bodies and surface a friendly "receipt not found upstream" error instead of a misleading "pdf-parse failed".
  • Real fetches are slow: Zemen's PDF backend takes 5–15 s per call even when healthy. links.et's outbound queue serialises calls (one in flight at a time) for this host and cooldowns aggressively on 429.

On this page