{"openapi":"3.1.0","info":{"title":"PeerPay Public API","version":"2026-06-01","description":"Merchant backend API for initiating PeerPay hosted P2P deposit orders and manual withdrawal requests.\n\nFor a production deposit integration:\n\n1. Create the deposit with one opaque, stable `Idempotency-Key` per logical order, such as your internal order ID or a persisted UUID, and reuse it exactly for retries.\n2. Save `deposit.id`, `merchant_order_id`, and `checkout_url`.\n3. Redirect the customer to hosted checkout.\n4. Receive and verify signed PeerPay webhooks.\n5. Treat webhooks as prompt change notifications and process them idempotently; delivery is at least once and ordering is not guaranteed.\n6. Read `GET /v1/deposits/{deposit_id}` whenever the latest state currently known to PeerPay must be confirmed.\n7. Periodically reconcile non-terminal deposits and use `GET /v1/deposits/{deposit_id}/events` for audit history or missed-delivery investigation.\n\nThe deposit resource is the source of truth for PeerPay's latest known deposit state. Webhook payloads may be delayed, duplicated, or older than the current resource. The event timeline is for audit and recovery. A checkout `return_url` is browser navigation only, and customer checkout status is not a merchant settlement signal."},"servers":[{"url":"https://api.peerpayment.org","description":"PeerPay API"}],"tags":[{"name":"Capabilities","description":"Discover advisory payment-method codes for this workspace."},{"name":"Deposits","description":"Create deposit orders and inspect current deposit state for merchant reconciliation."},{"name":"Deposit events","description":"Read immutable deposit event timelines for support and reconciliation."},{"name":"Withdrawals","description":"Create manual withdrawal requests and inspect current withdrawal state for merchant reconciliation."},{"name":"Withdrawal events","description":"Read immutable withdrawal event timelines for support and reconciliation."}],"security":[{"bearerAuth":[]}],"paths":{"/v1/capabilities":{"get":{"tags":["Capabilities"],"operationId":"getPublicCapabilities","summary":"Get integration capabilities","description":"Returns advisory ETB payment-method codes for the authenticated workspace. Deposit creation and hosted-checkout responses remain authoritative because routing capacity can change after this response.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current public integration capabilities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCapabilitiesResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"429":{"description":"Tenant rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/deposits":{"post":{"tags":["Deposits"],"operationId":"createDeposit","summary":"Create a deposit order","description":"Merchant backend initiates a deposit order. Generate one opaque Idempotency-Key per logical order and reuse it exactly for retries. PeerPay generates the merchant order ID, prepares available checkout payment options, and returns the deposit ID and hosted checkout URL; persist all three with the merchant order for webhook handling and reconciliation.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for deposit and withdrawal creation. Reuse the same key only for the same logical merchant order.","schema":{"type":"string","minLength":1,"maxLength":256}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepositRequest"}}}},"responses":{"200":{"description":"Idempotent replay of a completed create request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepositResponse"}}}},"201":{"description":"Deposit created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepositResponse"}}}},"400":{"description":"Invalid request or unsupported API version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"413":{"description":"Request body exceeds the configured limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"422":{"description":"Request body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"429":{"description":"Tenant rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"503":{"description":"No eligible deposit route or provider unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"504":{"description":"Verification provider timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}},"get":{"tags":["Deposits"],"operationId":"listDeposits","summary":"List deposits","description":"List deposits for the authenticated merchant with filters for operations, support, and reconciliation reporting.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"status","in":"query","required":false,"schema":{"$ref":"#/components/schemas/DepositStatus"}},{"name":"verification_status","in":"query","required":false,"schema":{"$ref":"#/components/schemas/VerificationStatus"}},{"name":"notification_status","in":"query","required":false,"schema":{"$ref":"#/components/schemas/NotificationStatus"}},{"name":"payment_method","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]}},{"name":"merchant_order_id","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":128}},{"name":"created_from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"created_to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"Deposit list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDepositsResponse"}}}},"400":{"description":"Invalid query or API version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"422":{"description":"Query parameters failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"429":{"description":"Tenant rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/deposits/{deposit_id}":{"get":{"tags":["Deposits"],"operationId":"getDeposit","summary":"Get a deposit","description":"Fetch the current deposit resource: the source of truth for the latest deposit state known to PeerPay. Use this endpoint after webhook notifications, when events may be delayed or out of order, and during periodic reconciliation of non-terminal deposits. Merchant wallet-credit state remains outside PeerPay.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"deposit_id","in":"path","required":true,"description":"PeerPay deposit ID returned by create deposit.","schema":{"type":"string","minLength":1,"maxLength":96}}],"responses":{"200":{"description":"Deposit details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetDepositResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"404":{"description":"Deposit not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/deposits/{deposit_id}/events":{"get":{"tags":["Deposit events"],"operationId":"listDepositEvents","summary":"List deposit events","description":"Read the immutable audit and recovery timeline for a deposit, including creation, assignment, reference submission, verification, and manual review outcomes. Use the deposit resource endpoint—not this timeline or a checkout return URL—when the latest current state must be confirmed.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"deposit_id","in":"path","required":true,"description":"PeerPay deposit ID returned by create deposit.","schema":{"type":"string","minLength":1,"maxLength":96}}],"responses":{"200":{"description":"Deposit event timeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositEventsResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"404":{"description":"Deposit not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/withdrawals":{"post":{"tags":["Withdrawals"],"operationId":"createWithdrawal","summary":"Create a withdrawal request","description":"Merchant backend initiates a fixed-amount manual withdrawal request. PeerPay generates the merchant withdrawal ID when omitted and returns a hosted checkout URL for customer destination confirmation. When the workspace enables Auto-Confirm Withdrawals, a complete destination (bank and account_number) is required, the withdrawal is confirmed and assigned in this request, and no checkout URL is returned.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for deposit and withdrawal creation. Reuse the same key only for the same logical merchant order.","schema":{"type":"string","minLength":1,"maxLength":256}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWithdrawalRequest"}}}},"responses":{"200":{"description":"Idempotent replay or an existing merchant withdrawal resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWithdrawalResponse"}}}},"201":{"description":"Withdrawal created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWithdrawalResponse"}}}},"400":{"description":"Invalid request or unsupported API version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"413":{"description":"Request body exceeds the configured limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"422":{"description":"Request body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"429":{"description":"Tenant rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"503":{"description":"Withdrawal provider unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"504":{"description":"Withdrawal provider timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}},"get":{"tags":["Withdrawals"],"operationId":"listWithdrawals","summary":"List withdrawals","description":"List withdrawals for the authenticated merchant with filters for operations, support, and reconciliation reporting.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"status","in":"query","required":false,"schema":{"$ref":"#/components/schemas/WithdrawalStatus"}},{"name":"merchant_withdrawal_id","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":128}},{"name":"merchant_customer_id","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":128}},{"name":"created_from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"created_to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"Withdrawal list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWithdrawalsResponse"}}}},"400":{"description":"Invalid query or API version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"422":{"description":"Query parameters failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/withdrawals/{withdrawal_id}":{"get":{"tags":["Withdrawals"],"operationId":"getWithdrawal","summary":"Get a withdrawal","description":"Fetch the current PeerPay state for a withdrawal request.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"withdrawal_id","in":"path","required":true,"description":"PeerPay withdrawal ID returned by create withdrawal.","schema":{"type":"string","minLength":1,"maxLength":96}}],"responses":{"200":{"description":"Withdrawal details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetWithdrawalResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"404":{"description":"Withdrawal not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}},"/v1/withdrawals/{withdrawal_id}/events":{"get":{"tags":["Withdrawal events"],"operationId":"listWithdrawalEvents","summary":"List withdrawal events","description":"Read the immutable event timeline for a withdrawal, including creation, customer confirmation, assignment, transfer submission, verification, and review outcomes.","security":[{"bearerAuth":[]}],"parameters":[{"name":"PeerPay-Version","in":"header","required":false,"description":"Requested PeerPay API version. Defaults to the current stable version when omitted.","schema":{"type":"string","const":"2026-06-01"}},{"name":"withdrawal_id","in":"path","required":true,"description":"PeerPay withdrawal ID returned by create withdrawal.","schema":{"type":"string","minLength":1,"maxLength":96}}],"responses":{"200":{"description":"Withdrawal event timeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WithdrawalEventsResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"403":{"description":"API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"404":{"description":"Withdrawal not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"}}}}}}}},"webhooks":{"transactionLifecycleEvent":{"post":{"tags":["Deposit events","Withdrawal events"],"summary":"Signed transaction lifecycle webhook","description":"PeerPay sends deposit, withdrawal, and webhook.test events at least once. Deduplicate by event ID and treat sequence as resource-local. Verify v2=HMAC_SHA256(secret, eventId + '.' + timestamp + '.' + rawBody) before parsing or acting on the body.","parameters":[{"name":"PeerPay-Event","in":"header","required":true,"schema":{"$ref":"#/components/schemas/MerchantWebhookEventType"}},{"name":"PeerPay-Event-Id","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":96}},{"name":"PeerPay-Delivery-Id","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":96}},{"name":"PeerPay-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^\\d{10,13}$"}},{"name":"PeerPay-Signature","in":"header","required":true,"description":"HMAC-SHA256 signature formatted as v2=<hex-signature>. Compute it from eventId.timestamp.rawBody using the unmodified request bytes.","schema":{"type":"string","pattern":"^v2=[a-f0-9]{64}$"}},{"name":"PeerPay-Api-Version","in":"header","required":true,"schema":{"type":"string","const":"2026-06-01"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantWebhookEvent"}}}},"responses":{"200":{"description":"Merchant endpoint accepted the webhook. PeerPay records delivery state, not merchant wallet-credit state."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Merchant API key created in the dashboard. Dashboard login uses Google and never accepts API keys as human credentials."}},"schemas":{"CapabilityMethod":{"type":"object","additionalProperties":false,"required":["code","display_name","account_type"],"properties":{"code":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"display_name":{"type":"string","minLength":1,"maxLength":160},"account_type":{"type":"string","enum":["mobile_wallet","bank_account","merchant_code"]}}},"PublicCapabilitiesResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"object","additionalProperties":false,"required":["currency","deposits","withdrawals"],"properties":{"currency":{"type":"string","const":"ETB"},"deposits":{"type":"object","additionalProperties":false,"required":["payment_methods"],"properties":{"payment_methods":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityMethod"}}}},"withdrawals":{"type":"object","additionalProperties":false,"required":["destination_institutions"],"properties":{"destination_institutions":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityMethod"}}}}}},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"ApiError":{"type":"object","additionalProperties":false,"required":["code","message","type","retryable"],"properties":{"code":{"type":"string","enum":["invalid_request","invalid_api_version","validation_failed","request_body_too_large","authentication_required","invalid_api_key","forbidden","resource_not_found","route_not_found","checkout_expired","checkout_selection_conflict","idempotency_conflict","commission_pending","commission_enabled","commission_conflict","platform_billing_conflict","capacity_override_required","credit_override_required","availability_override_required","agent_credit_account_closed","order_routing_unavailable","order_initiation_unavailable","deposit_not_cancellable","duplicate_transaction","reference_already_submitted","receiver_mismatch","amount_mismatch","currency_mismatch","transaction_too_old","verification_ambiguous","provider_unavailable","provider_timeout","image_processing_unavailable","image_processing_disabled","image_processing_credits_exhausted","unsupported_image","oversized_image","unreadable_image","incomplete_extraction","ocr_timeout","ocr_provider_failure","image_credit_top_up_pending","image_credit_top_up_reference_used","rate_limited","invariant_violation","internal_server_error"]},"message":{"type":"string","minLength":1},"type":{"type":"string","enum":["request_error","authentication_error","authorization_error","not_found_error","conflict_error","rate_limit_error","availability_error","provider_error","server_error"]},"retryable":{"type":"boolean"},"details":{"type":"object","additionalProperties":true}}},"ApiErrorEnvelope":{"type":"object","additionalProperties":false,"required":["error","meta"],"properties":{"error":{"$ref":"#/components/schemas/ApiError"},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"PaginationMeta":{"type":"object","additionalProperties":false,"required":["nextCursor","hasMore","limit"],"properties":{"nextCursor":{"oneOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"null"}]},"hasMore":{"type":"boolean"},"limit":{"type":"integer","minimum":1,"maximum":100}}},"PaginationResponseMeta":{"type":"object","additionalProperties":false,"required":["requestId","pagination"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}},"DepositStatus":{"type":"string","enum":["created","assigned","awaiting_transfer","reference_submitted","verification_pending","succeeded","failed","expired","cancelled","review_required"]},"WithdrawalStatus":{"type":"string","enum":["created","customer_confirmed","assigned","transfer_submitted","verification_pending","succeeded","failed","expired","cancelled","review_required"]},"VerificationStatus":{"type":"string","enum":["not_started","created","queued","running","succeeded","not_found","duplicate","receiver_mismatch","amount_mismatch","currency_mismatch","transaction_too_old","ambiguous","provider_error","timed_out","cancelled","review_required"]},"NotificationStatus":{"type":"string","enum":["not_ready","pending","delivered","retrying","exhausted","disabled"]},"MerchantWebhookEventType":{"type":"string","enum":["deposit.created","deposit.assigned","deposit.reference_submitted","deposit.verification_pending","deposit.succeeded","deposit.manually_succeeded","deposit.failed","deposit.manually_failed","deposit.expired","deposit.cancelled","deposit.review_required","deposit.review_resolved","withdrawal.created","withdrawal.customer_confirmed","withdrawal.assigned","withdrawal.transfer_submitted","withdrawal.verification_pending","withdrawal.verification_failed","withdrawal.succeeded","withdrawal.failed","withdrawal.expired","withdrawal.cancelled","withdrawal.review_required","withdrawal.review_resolved","webhook.test"]},"DepositEventType":{"type":"string","enum":["deposit.created","deposit.assigned","deposit.reference_submitted","deposit.verification_pending","deposit.succeeded","deposit.manually_succeeded","deposit.failed","deposit.manually_failed","deposit.expired","deposit.cancelled","deposit.review_required","deposit.review_resolved"]},"WithdrawalWebhookEventType":{"type":"string","enum":["withdrawal.created","withdrawal.customer_confirmed","withdrawal.assigned","withdrawal.transfer_submitted","withdrawal.verification_pending","withdrawal.verification_failed","withdrawal.succeeded","withdrawal.failed","withdrawal.expired","withdrawal.cancelled","withdrawal.review_required","withdrawal.review_resolved"]},"MerchantWebhookEvent":{"oneOf":[{"$ref":"#/components/schemas/DepositWebhookEvent"},{"$ref":"#/components/schemas/WithdrawalWebhookEvent"},{"$ref":"#/components/schemas/WebhookTestEvent"}]},"WebhookDepositObject":{"type":"object","additionalProperties":false,"required":["id","merchant_order_id","environment","status","currency","metadata","created_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"merchant_order_id":{"type":"string","minLength":1,"maxLength":128},"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128},"environment":{"type":"string","const":"live"},"status":{"$ref":"#/components/schemas/DepositStatus"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"]},"payment_method":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"assigned_agent":{"$ref":"#/components/schemas/AssignedAgent"},"assigned_account":{"$ref":"#/components/schemas/AssignedAccount"},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}}},"WebhookWithdrawalObject":{"type":"object","additionalProperties":false,"required":["id","merchant_withdrawal_id","merchant_customer_id","status","verification_status","amount","currency","metadata","created_at","expires_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"merchant_withdrawal_id":{"type":"string","minLength":1,"maxLength":128},"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128},"status":{"$ref":"#/components/schemas/WithdrawalStatus"},"verification_status":{"$ref":"#/components/schemas/VerificationStatus"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"]},"destination":{"type":"object","additionalProperties":false,"required":["bank","masked_account_number"],"properties":{"bank":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"masked_account_number":{"type":"string","minLength":1,"maxLength":96}}},"assigned_agent":{"$ref":"#/components/schemas/AssignedAgent"},"assignment_method":{"type":"string","enum":["automatic","manual"]},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"customer_confirmed_at":{"type":"string","format":"date-time"},"assigned_at":{"type":"string","format":"date-time"},"succeeded_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}}},"DepositWebhookEvent":{"type":"object","additionalProperties":false,"required":["id","type","api_version","sequence","created_at","data"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"type":{"$ref":"#/components/schemas/DepositEventType"},"api_version":{"type":"string","const":"2026-06-01"},"sequence":{"type":"integer","minimum":1},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","additionalProperties":false,"required":["object"],"properties":{"object":{"$ref":"#/components/schemas/WebhookDepositObject"}}}}},"WithdrawalWebhookEvent":{"type":"object","additionalProperties":false,"required":["id","type","api_version","sequence","created_at","data"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"type":{"$ref":"#/components/schemas/WithdrawalWebhookEventType"},"api_version":{"type":"string","const":"2026-06-01"},"sequence":{"type":"integer","minimum":1},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","additionalProperties":false,"required":["object"],"properties":{"object":{"$ref":"#/components/schemas/WebhookWithdrawalObject"}}}}},"WebhookTestEvent":{"type":"object","additionalProperties":false,"required":["id","type","api_version","sequence","created_at","data"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"type":{"type":"string","const":"webhook.test"},"api_version":{"type":"string","const":"2026-06-01"},"sequence":{"type":"integer","const":0},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","additionalProperties":false,"required":["object"],"properties":{"object":{"type":"object","additionalProperties":false,"required":["id","object","livemode"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"object":{"type":"string","const":"webhook_endpoint"},"livemode":{"type":"boolean","const":false}}}}}}},"CreateDepositRequest":{"type":"object","additionalProperties":false,"required":["merchant_customer_id","return_url"],"properties":{"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128,"examples":["player-99212"]},"amount":{"oneOf":[{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},{"type":"number","multipleOf":0.01,"examples":[1500,1500.25]}]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"],"default":"ETB","const":"ETB"},"payment_method":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"return_url":{"type":"string","format":"uri","examples":["https://merchant.example/deposits/return"]}}},"DepositPaymentOption":{"type":"object","additionalProperties":false,"required":["payment_method","institution_code","institution_name","masked_identifier","min_amount","max_amount"],"properties":{"payment_method":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"institution_code":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"institution_name":{"type":"string","minLength":1,"maxLength":160},"masked_identifier":{"type":"string","minLength":1,"maxLength":96},"min_amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"max_amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]}}},"AssignedAgent":{"type":"object","additionalProperties":false,"required":["id","display_name"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"display_name":{"type":"string","minLength":1,"maxLength":160}}},"AssignedAccount":{"type":"object","additionalProperties":false,"required":["id","institution_code","masked_identifier"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"institution_code":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"masked_identifier":{"type":"string","minLength":1,"maxLength":96}}},"DepositReference":{"type":"object","additionalProperties":false,"required":["masked","submitted_at"],"properties":{"masked":{"type":"string","minLength":1,"maxLength":128},"submitted_at":{"type":"string","format":"date-time"}}},"DepositVerification":{"type":"object","additionalProperties":false,"required":["provider","processing_status","verified"],"properties":{"provider":{"type":"string","const":"bank","description":"Verification source. PeerPay confirms the submitted transaction ID or reference against the assigned receiving account using bank and wallet APIs."},"provider_request_id":{"type":"string","minLength":1,"maxLength":128},"processing_status":{"$ref":"#/components/schemas/VerificationStatus"},"verified":{"type":"boolean"}}},"Deposit":{"type":"object","additionalProperties":false,"required":["id","merchant_order_id","status","verification_status","notification_status","currency","support_reference","payment_options","metadata","created_at","expires_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"merchant_order_id":{"type":"string","minLength":1,"maxLength":128},"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128},"status":{"$ref":"#/components/schemas/DepositStatus"},"verification_status":{"$ref":"#/components/schemas/VerificationStatus"},"notification_status":{"$ref":"#/components/schemas/NotificationStatus"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"]},"payment_method":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"checkout_url":{"type":"string","format":"uri"},"support_reference":{"type":"string","minLength":1,"maxLength":64},"payment_options":{"type":"array","items":{"$ref":"#/components/schemas/DepositPaymentOption"},"default":[]},"assigned_agent":{"$ref":"#/components/schemas/AssignedAgent"},"assigned_account":{"$ref":"#/components/schemas/AssignedAccount"},"reference":{"$ref":"#/components/schemas/DepositReference"},"verification":{"$ref":"#/components/schemas/DepositVerification"},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CreateDepositResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Deposit"},{"type":"object","required":["checkout_url","payment_options"]}]},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"GetDepositResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Deposit"},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"ListDepositsResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Deposit"}},"meta":{"$ref":"#/components/schemas/PaginationResponseMeta"}}},"DepositEvent":{"type":"object","additionalProperties":false,"required":["id","deposit_id","type","sequence","actor_type","data","created_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"deposit_id":{"type":"string","minLength":1,"maxLength":96},"type":{"$ref":"#/components/schemas/DepositEventType"},"sequence":{"type":"integer","minimum":0},"actor_type":{"type":"string","enum":["system","merchant","customer","platform"]},"reason_code":{"type":"string","minLength":1,"maxLength":96},"message":{"type":"string","minLength":1,"maxLength":500},"data":{"type":"object","additionalProperties":true,"default":{}},"created_at":{"type":"string","format":"date-time"}}},"DepositEventsResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DepositEvent"}},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"WithdrawalDestination":{"type":"object","additionalProperties":false,"required":["bank","account_number"],"properties":{"bank":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"account_number":{"type":"string","minLength":3,"maxLength":64},"account_holder_name":{"type":"string","minLength":1,"maxLength":160}}},"CreateWithdrawalRequest":{"type":"object","additionalProperties":false,"required":["merchant_customer_id","amount"],"properties":{"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128,"examples":["customer-1012"]},"merchant_withdrawal_id":{"type":"string","minLength":1,"maxLength":128,"description":"Optional legacy merchant correlation ID. Omit for PeerPay-generated IDs."},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"],"default":"ETB","const":"ETB"},"destination":{"$ref":"#/components/schemas/WithdrawalDestination"},"return_url":{"type":"string","format":"uri","examples":["https://merchant.example/withdrawals/return"]},"metadata":{"type":"object","additionalProperties":true,"default":{}}}},"Withdrawal":{"type":"object","additionalProperties":false,"required":["id","merchant_withdrawal_id","merchant_customer_id","status","verification_status","notification_status","amount","currency","support_reference","metadata","created_at","expires_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"merchant_withdrawal_id":{"type":"string","minLength":1,"maxLength":128},"merchant_customer_id":{"type":"string","minLength":1,"maxLength":128},"status":{"$ref":"#/components/schemas/WithdrawalStatus"},"verification_status":{"$ref":"#/components/schemas/VerificationStatus"},"notification_status":{"$ref":"#/components/schemas/NotificationStatus"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","examples":["1500.00"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","examples":["ETB"]},"destination":{"type":"object","additionalProperties":false,"required":["bank","masked_account_number"],"properties":{"bank":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9_:-]+$","examples":["telebirr"]},"masked_account_number":{"type":"string","minLength":1,"maxLength":96},"account_holder_name":{"type":"string","minLength":1,"maxLength":160}}},"checkout_url":{"type":"string","format":"uri"},"support_reference":{"type":"string","minLength":1,"maxLength":64},"assigned_agent":{"$ref":"#/components/schemas/AssignedAgent"},"failure_reason":{"type":"string","minLength":1,"maxLength":160},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"customer_confirmed_at":{"type":"string","format":"date-time"},"assigned_at":{"type":"string","format":"date-time"},"succeeded_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CreateWithdrawalResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Withdrawal","description":"Includes checkout_url for a new hosted-checkout withdrawal. Omitted when the workspace auto-confirms withdrawals, and on a merchant_withdrawal_id match that returns an existing withdrawal."},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"GetWithdrawalResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Withdrawal"},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"ListWithdrawalsResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Withdrawal"}},"meta":{"$ref":"#/components/schemas/PaginationResponseMeta"}}},"WithdrawalEvent":{"type":"object","additionalProperties":false,"required":["id","withdrawal_id","type","sequence","actor_type","data","created_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":96},"withdrawal_id":{"type":"string","minLength":1,"maxLength":96},"type":{"type":"string","minLength":1,"maxLength":120},"sequence":{"type":"integer","minimum":0},"actor_type":{"type":"string","enum":["system","merchant","customer","agent","provider"]},"reason_code":{"type":"string","minLength":1,"maxLength":96},"message":{"type":"string","minLength":1,"maxLength":500},"data":{"type":"object","additionalProperties":true,"default":{}},"created_at":{"type":"string","format":"date-time"}}},"WithdrawalEventsResponse":{"type":"object","additionalProperties":false,"required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WithdrawalEvent"}},"meta":{"type":"object","additionalProperties":false,"required":["requestId"],"properties":{"requestId":{"type":"string","minLength":1},"apiVersion":{"type":"string","const":"2026-06-01"},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}}}}}