ZAHLEN
Retry Decision API
![]()
Single decisions, batch decisions, response interpretation, and confidence
Audience
Merchants | Developers | Integration Engineers
Commercial workflow |
Payment Event -> Retry Decision -> Retry Outcome -> Investigation Run -> Reporting |
Zahlen API User Guide v1.0
Source baseline: zahlen_deploy_0616A.tar.gz | June 2026
Learning objectives |
By the end of this chapter, you should be able to choose the correct retry-decision contract, submit single and batch requests, interpret returned actions and explanations, and use confidence without treating it as a guarantee. |
The Retry Decision API evaluates payment evidence and returns an operational recommendation. It tells a merchant whether to retry now, wait for a scheduled retry day, stop retrying, or escalate for review. The API does not execute the payment and does not prove that a future retry will succeed.
Canonical retry schedule |
Zahlen is built around the fixed retry schedule Day 1, Day 2, Day 6, and Day 16. A returned retry day must be interpreted within this schedule. Client code must not invent extra retry days or move an authorization attempt outside the fixed sequence. |
Method | Path | Contract | Typical use |
POST | /v1/retry-decision | Legacy single decision | Existing integrations requiring the full historical request and response structure. |
POST | /v1/retry-decision/batch | Legacy batch decision | Evaluate up to 500 legacy- format events in one request. |
POST | /v1/_next/retry-decision | Next-generation single decision | Streamlined decision contract for integrations selected during onboarding. |
Do not mix contracts |
The legacy and next-generation endpoints use different request and response models. Select one approved contract and map it explicitly. Do not send fields from one model to the other or build client logic that silently combines both. |
Merchant-facing decision requests use the X-API-Key header. Tenant and merchant ownership are resolved from the authenticated key. Decision routes support Idempotency-Key where documented by the implementation; use one stable key for one logical decision operation.
X-API-Key: zk_live_REPLACE_ME
Idempotency-Key: order-8842-attempt-2 Content-Type: application/json
A network timeout can occur after Zahlen has created the decision but before the client receives the response.
Retrying with the same idempotency key allows the server to recognize the same logical operation.
A retry for the same operation must preserve the same request body and key.
A genuinely new attempt should use a new idempotency key.
Persist the returned request_id, decision_id, and idempotent replay indicator.
API retry versus payment retry |
Retrying an HTTP request is not the same as retrying the customer payment. HTTP retries must be safe and idempotent. Payment retries must occur only on the fixed Day 1, Day 2, Day 6, and Day 16 schedule. |
decision:{merchant_order_id}:{billing_cycle_id}:attempt:{attempt_number}
Example:
decision:order-8842:cycle-2026-06:attempt:2
POST /v1/retry-decision uses RetryDecisionRequest. Extra fields are forbidden. Five fields are required; the remaining fields provide issuer, processor, transaction, and policy context.
Field | Type | Purpose |
payment_token | string | Merchant-safe token representing the payment method. |
billing_cycle_id | string | Stable identifier for the subscription or billing cycle. |
event_ts_iso | string | Timestamp of the observed authorization or decline event. |
cycle_start_ts_iso | string | Start timestamp for the billing cycle. |
paymentech_code | string | Processor response or decline code expected by the legacy contract. |
curl -sS -X POST "$ZAHLEN_BASE_URL/v1/retry-decision" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ZAHLEN_API_KEY" \
-H "Idempotency-Key: cycle-2026-06-attempt-2" \
-d '{
"payment_token": "tok_001", "billing_cycle_id": "cycle_2026_06", "event_ts_iso": "2026-06-16T12:00:00Z", "cycle_start_ts_iso": "2026-06-01T00:00:00Z", "paymentech_code": "51",
"card_network": "VISA", "amount": 29.99,
"currency": "USD", "attempt_day_in_cycle": 2, "subscription_id": "sub_001"
}'
Optional legacy fields include country and issuer context, BIN values, processor, recommended action evidence, outcome flags, order/invoice/subscription references, transaction kind, spike-alert controls, and deterministic or AI external-change mode. Send only fields your integration can populate accurately.
RetryDecisionResponse is organized into decision, reasoning, signals, explanations, metadata, and an optional policy block. This structure separates the control instruction from the evidence and version information used to produce it.
Field | Allowed values / type | Interpretation |
action | RETRY_NOW, WAIT, STOP, ESCALATE | Primary operational instruction. |
state | RETRY_SCHEDULED, RETRY_NOW_ELIGIBLE, DO_NOT_RETRY, REQUIRES_REVIEW | More specific decision state. |
recommended_next_attempt_day_in_cycle | integer or null | Next permitted retry day when one is scheduled. |
recommended_next_attempt_at | string or null | Timestamp representation when provided. |
Section | Important fields | Use |
reasoning | reason_code, reason, confidence, guidance_source | Explain why the action was selected. |
signals | seen_before, truth_confidence_band, external_status, analysis_mode | Describe supporting evidence and prior observation. |
explanations | array | Present additional human-readable explanation when available. |
meta | request_id, versions, processed_at, idempotency_key | Support traceability and reproducibility. |
policy | applied, merchant_id, rule_id, source | Show whether a merchant policy affected the result. |
Action is the control signal |
Base automated workflow on the explicit action and state. Reason text is designed for explanation and support, not for fragile string matching. |
POST /v1/retry-decision/batch accepts BatchRetryDecisionRequest. The events array can contain up to 500 legacy decision requests. This is a schema ceiling, not a guaranteed throughput target.
{
"events": [
{
"payment_token": "tok_001", "billing_cycle_id": "cycle_2026_06_a", "event_ts_iso": "2026-06-16T12:00:00Z", "cycle_start_ts_iso": "2026-06-01T00:00:00Z", "paymentech_code": "51",
"attempt_day_in_cycle": 1
},
{
"payment_token": "tok_002", "billing_cycle_id": "cycle_2026_06_b", "event_ts_iso": "2026-06-16T12:01:00Z", "cycle_start_ts_iso": "2026-06-01T00:00:00Z", "paymentech_code": "91",
"attempt_day_in_cycle": 2
}
]
}
Field | Meaning | Client guidance |
results | One evaluated item per returned decision | Correlate each result with the original event identity; do not rely only on array position unless contractually guaranteed. |
meta | Batch request/version metadata | Log request ID, processing time, and version fields. |
count | Number of returned results | Compare with the intended and submitted event counts. |
Split larger workloads into bounded batches below the 500-item maximum.
Do not retry the whole batch blindly after a partial or uncertain result.
Use stable event and billing-cycle identifiers so individual items can be reconciled.
Monitor batch latency and 429 activity; batching does not bypass rate limits or quotas.
POST /v1/_next/retry-decision uses NextRetryDecisionPayload. Only attempt_number is required, and it must be at least 1. The streamlined contract can carry decline, issuer, amount, timing, authorization, and recurring-payment context. Extra fields are forbidden.
Field | Type | Required | Notes |
attempt_number | integer | Yes | Minimum 1; identify the attempt within the merchant workflow. |
token | string or null | No | Merchant-safe payment token. |
decline_code | string or null | No | Observed decline or processor response code. |
issuer_bin | string or null | No | Issuer identification context; never send a full PAN. |
issuer_name / issuer_country / card_brand | string or null | No | Issuer and card context. |
amount_minor | integer or null | No | Nonnegative integer in minor currency units. |
currency | string or null | No | Currency code paired with amount_minor. |
decline_category | string or null | No | Merchant or processor classification when available. |
event_timestamp | string or null | No | Observed event time. |
authorization_id / authorization_latency_ms | string / number or null | No | Processor correlation and latency. |
merchant_category_code / recurring_indicator / transaction_initiator | string or null | No | Transaction context. |
curl -sS -X POST "$ZAHLEN_BASE_URL/v1/_next/retry-decision" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ZAHLEN_API_KEY" \
-H "Idempotency-Key: order-8842-attempt-2" \
-d '{
"token": "tok_001", "attempt_number": 2,
"decline_code": "51",
"issuer_bin": "411111", "card_brand": "VISA", "amount_minor": 2999, "currency": "USD",
"event_timestamp": "2026-06-16T12:00:00Z"
}'
NextRetryDecisionResponse provides a flat decision envelope with traceability, explanation, policy, and issuer context. Nullable fields must be represented explicitly in typed clients.
Field | Type | Interpretation |
request_id | string | Request correlation for logs and support. |
decision_id | string | Durable identifier for this decision. |
merchant_id | string | Resolved merchant context. |
token | string or null | Returned merchant token correlation. |
attempt_number | integer | Attempt evaluated. |
decision | string | Primary decision value; use as the control signal. |
retry_day | integer or null | Scheduled retry day, when applicable. |
reason_code | string | Machine-readable explanation category. |
reason_detail | string or null | Human-readable explanation. |
policy_source / matched_policy_id | string or null | Policy provenance. |
confidence | number or null | Evidence-strength score, not a guarantee. |
created_at | string | Decision creation timestamp. |
idempotent_replay | boolean | Whether the result was served as an idempotent replay. |
issuer_context | object or null | Extensible issuer evidence. |
explainability_sections | array | Additional structured explanations. |
decision_trace | object | Machine-readable trace details. |
Nullable retry day |
A null retry_day can be correct. It may accompany a stop, escalation, or other decision that intentionally does not schedule another payment attempt. Do not substitute the next calendar day. |
When a retry is authorized, client validation should confirm that the returned retry day is one of the canonical schedule points: 1, 2, 6, or 16. An unexpected value should be logged and escalated rather than silently executed.
ALLOWED_RETRY_DAYS = {1, 2, 6, 16}
if response["retry_day"] is not None:
if response["retry_day"] not in ALLOWED_RETRY_DAYS:
raise ValueError("Unexpected retry day; do not execute payment")
Confidence describes the strength and consistency of the evidence behind a decision. It does not measure the dollar importance of the account and does not promise authorization success. The legacy response uses LOW, MED, or HIGH; the next-generation response exposes a nullable numeric confidence value.
Confidence concept | Correct interpretation | Incorrect interpretation |
LOW / lower numeric confidence | Limited, sparse, new, or conflicting evidence; preserve decision but increase review and monitoring. | The decision is automatically wrong. |
MED / middle-range confidence | Useful evidence with some uncertainty; follow the decision and retain explanation. | There is a fixed universal success percentage. |
HIGH / higher numeric confidence | Strong, consistent evidence for the selected action. | The payment is guaranteed to recover. |
Always honor the explicit decision before interpreting confidence.
Expose reason and confidence to operators who investigate exceptions.
Use confidence to select review, monitoring, or escalation intensity.
Do not create undocumented confidence thresholds unless they are part of an approved merchant policy.
Record confidence with the decision ID, request ID, rules or policy source, and timestamp.
High confidence does not replace outcome reporting |
A high-confidence retry recommendation still requires a real processor attempt and a reported retry outcome. Decision quality is learned and measured only when the merchant closes the loop. |
Build a schema-valid request using the approved legacy or next-generation contract.
Send X-API-Key and a stable Idempotency-Key for the logical operation.
Persist the complete response before scheduling or executing a payment action.
Read the explicit decision or action and state.
Validate any retry day against Day 1, Day 2, Day 6, and Day 16.
Use reason, confidence, policy, and issuer context for explanation and review.
Execute the permitted payment attempt in the merchant payment stack.
Report the observed result through POST /v1/retry-outcome.
decision = response["decision"]
retry_day = response.get("retry_day")
if decision in {"STOP", "DO_NOT_RETRY"}: cancel_future_payment_attempts()
elif decision in {"ESCALATE", "REQUIRES_REVIEW"}: open_manual_review(response)
elif retry_day in {1, 2, 6, 16}: schedule_payment_attempt(day=retry_day)
else:
hold_and_alert("Decision cannot be safely executed")
Recommendation is not settlement |
The Retry Decision API recommends operational behavior. The processor result and settlement status remain separate facts and must be reported through the outcome workflow. |
Status | Meaning | Client response |
400 | Malformed or business-invalid request | Correct the request; do not blindly retry. |
401 | Missing or invalid API key | Check secret injection, key status, and environment. |
403 | Authenticated but not authorized | Check plan, endpoint access, or contract. |
409 | Conflict or idempotency mismatch | Compare the original body and idempotency key. |
422 | Schema validation failed | Fix required, typed, constrained, or unknown fields. |
429 | Rate or quota enforcement | Honor Retry-After when present and use backoff with jitter. |
500/503 | Server or dependency failure | Retry with the same idempotency key and bounded backoff. |
The client implements exactly one approved decision contract per integration path.
Unknown request fields are rejected locally before transmission.
API keys and idempotency keys are centralized and never logged as secrets.
Every response is logged using request_id and decision_id.
Nullable retry day, confidence, policy, and issuer context are handled safely.
Only Day 1, Day 2, Day 6, and Day 16 can create payment attempts.
Batch reconciliation does not rely on fragile assumptions.
429 and transient 5xx retries are bounded, jittered, and idempotent.
Every executed recommendation is followed by outcome reporting.
Chapter summary |
The Retry Decision API converts payment evidence into an explainable action. Reliable integrations keep legacy and next-generation contracts separate, use idempotency for safe HTTP retries, treat confidence as evidence strength, obey the fixed Day 1/2/6/16 schedule, and report the actual outcome. |