Zid customer-wallet integrations are safest when your application treats every credit or debit as a financial command with its own operation ID, durable state and reconciliation path. Never interpret HTTP 200 from a bulk request as complete success: Zid explicitly documents that bulk credit and debit can return 200 even when every item fails, so the result contract is the per-customer succeeded and failed lists plus failed_count. Persist the intent before calling Zid, record each returned transaction ID, retry only unresolved customers and verify balances and transactions afterward.
This practical guide is based on Zid's official customer-wallet documentation, verified on 20 October 2026. It is an engineering explanation, not an announcement and not financial advice. Zid documents the platform behavior; the operation ledger, idempotency controls, approval workflow and reconciliation design below are implementation recommendations.
Start with platform eligibility and permission boundaries
Zid documents customer-wallet APIs for stores operating in SAR, AED, QAR, KWD, BHD or OMR. A store using another currency receives a GCC-only availability error. Validate store eligibility during onboarding rather than discovering the restriction in the middle of a campaign.
Read and write access are separate. Listing wallets, balances, pending cashback and analytics uses the customer-wallet read scope. Credit and debit operations require the write scope, and Zid says write access needs prior approval for an allowed use case during app review. Do not request write access merely because the application might need it later. Keep read-only reporting, campaign preparation and balance verification usable without the mutation credential.
Never accept store ID, customer ID, amount or reason directly from an untrusted client and forward them to Zid. Derive the merchant from the authenticated installation, authorize the operator, validate currency and amount in integer minor units inside your system, and record who approved the command. Wallet credit is value creation; wallet debit removes value. Both need stricter controls than a normal profile update.
Model a wallet adjustment as a command, not a button click
Create one internal operation before the outbound request. A useful operation record contains merchant_id, operation_id, type, amount_minor, currency, reason_code, expiry_at, requested_by, approved_by, source_reference and status. Each target customer gets a child row with its own state: prepared, submitted, succeeded, failed, unknown or reconciled.
The operation ID is your idempotency boundary even when the upstream request does not expose an idempotency-key field. Put a unique constraint on merchant_id plus operation_id and another on merchant_id plus source_reference plus customer_id plus adjustment_type. A repeated worker, double click or queue redelivery then finds the existing command instead of issuing another credit.
Do not collapse amount and balance. An adjustment is an event; the balance is a platform-owned projection at a point in time. Store the requested amount and the upstream transaction ID, but fetch the current balance when a workflow needs authoritative balance information. Zid's balance response distinguishes total_balance, available balance, held_balance, currency and pending_cashback, so a single number cannot represent every state.
Treat bulk HTTP 200 as transport success only
Zid's bulk credit and bulk debit endpoints apply the same amount to several customer IDs. Their documented response contains succeeded and failed arrays plus total, succeeded_count and failed_count. The documentation warns that the endpoint may return 200 even when all adjustments fail. Therefore HTTP status answers only whether you received a structured response; it does not answer whether business work succeeded.
Validate four invariants before marking the batch complete: total equals the target count, succeeded_count matches usable succeeded entries, failed_count matches failed entries, and every requested customer appears exactly once. If the response contains duplicate customer IDs, missing targets or count disagreement, move the operation to needs_review instead of guessing.
Commit each customer result independently. Save Zid's transaction_id for success. Save the platform error for failure. A mixed response is partially_succeeded, not failed and not succeeded. Retry a new sub-batch containing only explicitly failed customers whose error is classified as transient. Never resend the entire original batch, because that can credit successful customers again.
operation = create_or_get_operation(source_reference)
targets = reserve_unique_targets(operation, customer_ids)
response = zid.bulk_credit(targets.prepared, amount, reason, expires_at)
assert response.total == targets.prepared.count
for item in response.succeeded:
mark_succeeded(item.customer_id, item.transaction_id)
for item in response.failed:
mark_failed(item.customer_id, item.error)
if response.failed_count > 0:
enqueue_retry_only_for_classified_transient_failures()
enqueue_reconciliation(operation.id)Separate definite failure from unknown outcome
A documented failed entry is a definite business failure for that customer. A timeout, disconnected client or malformed response is different: Zid may have applied the adjustment even though your application did not receive the transaction ID. Mark that target unknown. Blind retry is unsafe because the second call may create a second adjustment.
Reconcile unknown outcomes through the customer-wallet transaction history and current balance. Match on customer, direction, amount, reason and a narrow time window, then store the matching Zid transaction ID. If the evidence is ambiguous, require operator review. Balance difference alone is insufficient because cashback, redemption or another manual adjustment can change the same wallet concurrently.
For high-value or large campaigns, use maker-checker approval: one authorized user prepares the command and another approves it. Freeze the immutable command payload after approval. Changes to amount, audience, reason or expiry create a new operation ID and a new approval decision.
Build campaigns as bounded, observable jobs
Large audiences should be expanded into durable target rows before sending. Use small batches, bounded concurrency and the platform's rate-limit guidance. A campaign worker claims a limited set of prepared targets, submits them, persists the response and releases the lease. A crash then leaves recoverable states rather than an untraceable partially completed audience.
Zid's list-wallet endpoint is paginated and can filter by minimum or maximum balance and expiring credit. Use it for selection or reconciliation without loading the entire store into memory. Snapshot the campaign selection criteria and timestamp. If membership must remain fixed, materialize the customer IDs before approval; do not re-run a moving filter halfway through execution.
The pending-cashback endpoint uses cursor pagination. Preserve the exact next cursor as an opaque checkpoint. Do not calculate offsets or assume page order. For exports, Zid documents an asynchronous export request; use it for human review or large audit extracts, not as a synchronous dependency inside checkout.
Reconcile balances, transactions and campaign totals
Run reconciliation at two levels. Per customer, confirm that every succeeded internal target has a Zid transaction ID and that unknown targets are resolved or escalated. Per campaign, compare requested, succeeded, failed, unknown and reconciled counts, plus requested and applied monetary totals. The counts must add up to the original audience.
Use wallet analytics as an aggregate signal, not a replacement for the command ledger. Zid documents store-level metrics for outstanding balance, cashback, redemptions and ROI, and analytics by cashback rule. Those metrics can reveal drift or unexpected campaign impact, but they cannot prove that one customer's adjustment executed exactly once.
Alerts should cover unknown outcomes older than the reconciliation objective, repeated failures by error class, count mismatches, unusual total value, write calls by unexpected actors, and a campaign stuck without progress. Keep audit retention aligned with contractual and regulatory requirements, and minimize copied customer data.
Know when not to use a wallet adjustment
Do not use wallet credit as a substitute for reversing a payment, refunding an order or correcting accounting revenue unless the business policy explicitly defines that treatment. A promotional credit, cashback reward, refund and manual goodwill adjustment have different ownership, expiry and reporting consequences even if all increase a wallet balance.
Do not debit a wallet to repair an internal reporting discrepancy. First establish the authoritative transaction and obtain approval. Never allow negative amounts to choose direction; expose separate credit and debit commands with separate authorization and reason codes.
The production contract is straightforward: verify eligibility and approved scopes, create one durable command, materialize unique targets, evaluate every item in the bulk response, retry only definite transient failures, reconcile unknown outcomes against transactions and preserve an operator-visible audit trail. That turns a convenient bulk API into a controlled wallet operation rather than an irreversible campaign button.
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
- Zid Docs — Bulk Credit Customer Wallets, verified 20 October 2026
- Zid Docs — Bulk Debit Customer Wallets, verified 20 October 2026
- Zid Docs — Credit Customer Wallet, verified 20 October 2026
- Zid Docs — Debit Customer Wallet, verified 20 October 2026
- Zid Docs — List Customer Wallets, verified 20 October 2026
- Zid Docs — Get Customer Wallet Balance, verified 20 October 2026
- Zid Docs — Customer Pending Cashbacks, verified 20 October 2026
- Zid Docs — Customer Wallet Transactions, verified 20 October 2026
- Zid Docs — Wallet Analytics, verified 20 October 2026
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




