Answers to the most common questions from developers building on the KRA eTIMS OSCU and VSCU APIs.
resultCd 901 means Not a Valid Device. The device serial number (dvcSrlNo) you submitted is not registered or approved in KRA's system. This is unrecoverable without KRA intervention — you cannot self-register a serial.
In sandbox, try the pre-provisioned serial dvcv1130. In production, email timsupport@kra.go.ke with subject: "Request for OSCU Device Registration — [Company Name] — PIN: [Your PIN]". Budget several business days for a response.
resultCd 921 means Sales Invoice Sequence Violation. You called saveInvoice before saveSales on the VSCU path. VSCU requires two sequential calls in this exact order:
1. POST /trnsSales/saveSales — transaction metadata
2. POST /trnsSales/saveInvoice — invoice detail + receipt signature
OSCU combines both into a single POST /saveTrnsSalesOsdc call. These paths cannot be mixed. Every developer who builds from OSCU documentation and switches to VSCU hits this.
resultCd 994 means Duplicate Record. On the OSCU path, treat 994 on retry or offline queue replay as an idempotent success — the invoice was already registered. Do not raise an exception.
On the VSCU JAR path, the same situation returns 899 instead, not 994 (verified against the live VSDC 2.0.6 sandbox, 2026-07-06) — do not wait for a 994 that never comes. Either way, the original submission likely succeeded and KRA consumed the receipt counter; verify via the corresponding selectTrnsSales endpoint before re-signing. Invoice numbers can never be reused even after cancellation.
"000" is the documented OSCU spec code (§4.18), and live signings against the real VSDC 2.0.6 sandbox JAR also return "000" (verified 2026-07-06). But "0000" has been observed live from GavaConnect, and "00" has been reported for the VSCU JAR (unconfirmed by direct observation).
Never hardcode a single string. Always check membership in {"0", "00", "000", "0000"} so every variant is treated as success.
The VSCU JAR stops issuing receipt numbers exactly 24 hours after the last successful connection to KRA servers, with no warning signal. When this ceiling is reached, the failure surfaces as a TCP connection refusal or HTTP 503 — indistinguishable from a JAR crash (resultCd 894).
Track your last successful connectivity timestamp at the application layer. Ensure the JAR reconnects to KRA at least once every 24 hours.
cmcKey is the Communication Certificate Key issued by KRA during device initialization. It is issued exactly once with no rotation mechanism. You must persist it immediately after the init response.
Store it AES-256-GCM encrypted at rest — never in plaintext. Redact it before any logging, as any log line containing the full init response permanently leaks this key. If lost, you must contact KRA to regenerate it. There is no self-service reset path.
OSCU (Online Sales Control Unit) communicates directly with KRA servers via a single combined API call per sale (POST /saveTrnsSalesOsdc). VSCU (Virtual Sales Control Unit) is a local JAR process that signs receipts offline and syncs with KRA, requiring two sequential calls per sale (saveSales then saveInvoice).
VSCU-specific constraints: 24-hour connectivity ceiling, local storage limit (resultCd E11), and separate device serials. OSCU and VSCU serials cannot be cross-used — doing so causes resultCd 903 or 905.
KRA eTIMS uses five tax band codes:
A — Exempt, 0% (no input VAT credit)
B — Standard VAT, 16%
C — Zero-Rated, 0% (input credit allowed)
D — Non-VAT, 0% (outside the VAT Act entirely)
E — Special Rate, 8% (petroleum products, LPG)
Warning: the official §4.1 Tax Type table contains a known error — some spec versions state B=16.00% while live API samples have returned B=18.00%. Always fetch rates from selectCodes at runtime. Never hardcode tax rates.
Yes. All 15 fields — taxblAmtA/B/C/D/E, taxRtA/B/C/D/E, taxAmtA/B/C/D/E — are required in every transaction request, even for single-category businesses. Set unused fields to 0 rather than omitting them. Omitting any of these fields causes resultCd 910.
No. resultCd 902 means Device Already Installed — the device serial has already been initialized and an active cmcKey exists. This is idempotent, not a hard failure.
The 902 response does NOT return the cmcKey — verified live against the KRA sandbox (2026-07-04), the body is {"resultCd":"902","resultMsg":"This device is installed","data":null}. Use the cmcKey you persisted at first initialization. If you redeployed and lost it, contact timsupport@kra.go.ke to de-register the serial so the device can be re-initialized.
resultCd 001 means No Search Result. It is not a failure — no records exist matching your query yet. New integrations querying for purchases or stock on day one hit this constantly.
Most public SDKs check if resultCd != "000" and raise an exception here — that is wrong. Treat 001 as an empty list [], not an error. Only raise if your business logic requires at least one result.
KRA eTIMS uses two formats depending on the field:
YYYYMMDD — date-only fields: salesDt, pchsDtYYYYMMDDHHmmss — datetime fields: cfmDt, stockRlsDt
Sending a date in the wrong format causes resultCd 910.
No. Invoice numbers are sequential integers and can never be reused, even after cancellation. They must be strictly monotonically increasing (1, 2, 3…) — not string identifiers like INV001. Reusing an invoice number causes resultCd 994 (Duplicate Record).
In ONLINE mode the VSCU JAR relays each saveSales call synchronously to KRA's servers before responding. Live signings against the KRA sandbox take about 2.0–2.2 seconds (measured 2026-07-06). Any client timeout at or below ~2 seconds therefore fails every real signing — and dangerously: KRA still consumes the receipt counter even though your client never sees the response.
Use a client timeout of 15 seconds or more, and treat a timeout as "outcome unknown", not "failed": check whether the receipt exists (select endpoint or receipt-counter arithmetic) before replaying, because a replay of the same invoice number returns 899 on the VSCU path.
No. The VSCU JAR returns HTTP 200 for error responses — both 899 (client catch-all) and 902 (device already installed) arrive as HTTP 200 with the error in the JSON body (verified live against VSDC 2.0.6, 2026-07-04). GavaConnect does the same for some malformed requests.
Parse resultCd from the body on every response. Status-code-based error handling silently treats every KRA error as success.
The spec's §3.3.6 JSON sample names the receipt publication timestamp VSCURcptPbctDate, but the real VSDC 2.0.6 JAR returns the field as vsdcRcptPbctDate (verified live 2026-07-06: {"data":{…,"vsdcRcptPbctDate":"20260706202219",…}}). A strict mapping against the spec name silently yields null.
Parse the timestamp case-insensitively or register aliases for vsdcRcptPbctDate, VSDCRcptPbctDate, and VSCURcptPbctDate. The real JAR also returns mrcNo in the data object.
Cache code list and item classification responses — call selectCodes and selectItems once at initialization and refresh on a schedule, not on every transaction. When you hit a 990, back off and retry with exponential delay. Never poll these endpoints in a tight loop.
resultCd E04 means Device / Branch Not Found. The bhfId in your request does not exist in KRA's system for your TIN. Verify your bhfId against what was assigned during registration — the default sandbox bhfId is "00".
This is a letter-prefixed code absent from all official spec documents. It is only confirmed through live GavaConnect production observation.