openapi: 3.2.0
info:
title: zerohash Payins API
description: '
### Authentication
zerohash Uses HMAC SHA-256 verification to ensure the authenticity of API requests, follow instructions by link [https://docs.zerohash.com/reference/api-authentication](https://docs.zerohash.com/reference/api-authentication)
<a href="/zh-swagger.json">Download zerohash OpenAPI Schema as JSON</a>
'
version: 1.7.0
servers:
- url: https://api.cert.zerohash.com
description: Certification API server
security:
- apiKey: []
apiPassphrase: []
tags:
- name: Payins
description: Power your checkout with crypto and stablecoins
paths:
/pay/rfq:
post:
tags:
- Payins
summary: Request a payin quote
description: "\nRetrieves a quote for a pay event for a given participant code and asset.\nThe response includes a deposit address and rate at which a subsequent on-chain deposit will be honored.\n\nThe quote has an expiry timestamp (`price_expire_ts`) — for volatile assets, the platform must execute the deposit before this time,\nor a new quote can be requested for the same payment to refresh the price.\nFor stablecoins, `price_expire_ts` will be `null`.\n\nThe `request_id` you send (via the `X-Request-Id` header) is the idempotency key for this endpoint: re-submitting the same `request_id` returns the existing active quote — same `transaction_id`, deposit address, and rate — instead of creating a new one.\n\nUse the returned `transaction_id` as the path parameter when calling `POST /pay/{id}/rfq` to refresh a quote.\n\nAfter the on-chain deposit settles or fails, a `crypto_pay_status_changed` webhook reports the outcome (`success` + `status_reason_code`).\n "
parameters:
- $ref: '#/components/parameters/Signature'
- $ref: '#/components/parameters/Timestamp'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PostPayRfqBody'
responses:
'200':
description: Pay quote created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/PostPayRfqResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Code400'
'401':
description: 'The platform, jurisdiction, or asset is not enabled for the Pay product. The `{"error": ...}` body is one of "Your Platform is not configured to use the Pay product", "The State you live in is not authorized to use this product", or "Asset not enabled for pay".'
content:
application/json:
schema:
type: object
required:
- error
properties:
error:
type: string
'403':
description: 'The transaction is not authorized. Possible causes: manual approval is required before this transaction can proceed (Level 4 shoppers — the body contains a transaction_id identifying the approval review), or the shopper has exceeded their daily spend limit at the current authorization level. A generic `{"error":"Transaction not authorized"}` body is returned when the transaction is hard-denied (no remediation).'
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/PayinsAuthorizationManualApprovalError'
- $ref: '#/components/schemas/PayinsAuthorizationLimitError'
- $ref: '#/components/schemas/PayinsTransactionNotAuthorizedError'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/Code404'
'409':
description: Shopper KYC requirements are not met for this transaction.
content:
application/json:
schema:
$ref: '#/components/schemas/PayinsAuthorizationKycError'
'422':
description: 'The transaction could not be authorized. The `{"error": ...}` body is either "Participant is not authorized to transact" (the participant/merchant relationship or state precludes authorization) or "Transaction not authorized" (the authorizer returned an unrecognized status).'
content:
application/json:
schema:
$ref: '#/components/schemas/PayinsTransactionNotAuthorizedError'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Code500'
'503':
description: Service Unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Code503'
/pay/{id}/rfq:
post:
tags:
- Payins
summary: Refresh a payin quote
description: "\nRefreshes an existing pay quote identified by its transaction ID.\nThis is used when a previously issued quote is near to expire and the platform wants a new rate for the same deposit address.\nAlready expired quotes can't be refreshed.\n\nThe response format is identical to `POST /pay/rfq` — it returns an updated rate, expiry timestamp, and the same deposit address.\n "
parameters:
- $ref: '#/components/parameters/Signature'
- $ref: '#/components/parameters/Timestamp'
- name: id
in: path
description: The transaction ID (UUID) returned from the original POST /pay/rfq call
required: true
schema:
type: string
format: uuid
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PostPayRfqByIdBody'
responses:
'200':
description: Pay quote refreshed successfully
content:
application/json:
schema:
$ref: '#/components/schemas/PostPayRfqResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Code400'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Code403'
'404':
description: Transaction not found
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Transaction not found
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Code500'
'503':
description: Service Unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Code503'
/pay/transactions:
get:
tags:
- Payins
summary: List payin transactions
description: "\nRetrieves a paginated list of pay transactions for the authenticated platform.\nResults can be filtered by participant, asset, deposit status, and timestamps.\nEach deposit also emits a `crypto_pay_status_changed` webhook when it settles or fails.\n\n**Note:** Some filtering capabilities may not be available for all platforms. Contact support if you need access to additional filters.\n "
parameters:
- $ref: '#/components/parameters/Signature'
- $ref: '#/components/parameters/Timestamp'
- name: participant_code
in: query
description: Filter by participant code (6-character alphanumeric)
required: false
schema:
type: string
- name: transaction_id
in: query
description: Filter by specific transaction ID (UUID format)
required: false
schema:
type: string
- name: client_reference_id
in: query
description: Filter by client-provided reference ID. This filter may not be available for all platforms. Contact support if you need access.
required: false
schema:
type: string
- name: pay_asset
in: query
description: Filter by the underlying crypto asset (e.g., BTC, ETH, USDC)
required: false
schema:
type: string
- name: deposit_address
in: query
description: Filter by the deposit address provided by zerohash for this payment.
required: false
schema:
type: string
- name: success
in: query
description: Filters transactions by successful status. This is a boolean field, please specify either `true` or `false`
required: false
schema:
type: string
enum:
- 'true'
- 'false'
- name: page
in: query
description: Page number (1-indexed, max 5 digits)
required: false
schema:
type: string
pattern: ^[1-9][0-9]*$
- name: page_size
in: query
description: The number of results returned per page, with a maximum of 50 transactions per page
required: false
schema:
type: string
pattern: ^[1-9][0-9]*$
- name: deposit_timestamp_gte
in: query
description: Deposit timestamp greater than or equal to this value (inclusive). Unix milliseconds (13-19 digits). This filter may not be available for all platforms. Contact support if you need access.
required: false
schema:
type: string
- name: deposit_timestamp_gt
in: query
description: Deposit timestamp strictly greater than this value (exclusive). Unix milliseconds (13-19 digits). This filter may not be available for all platforms. Contact support if you need access.
required: false
schema:
type: string
- name: deposit_timestamp_lt
in: query
description: Deposit timestamp strictly less than this value (exclusive). Unix milliseconds (13-19 digits). This filter may not be available for all platforms. Contact support if you need access.
required: false
schema:
type: string
- name: deposit_timestamp_lte
in: query
description: Deposit timestamp less than or equal to this value (inclusive). Unix milliseconds (13-19 digits). This filter may not be available for all platforms. Contact support if you need access.
required: false
schema:
type: string
- name: merchant_participant_code
in: query
description: Filter by merchant participant code.
required: false
schema:
type: string
responses:
'200':
description: List of pay transactions with pagination metadata
content:
application/json:
schema:
$ref: '#/components/schemas/GetPayTransactionsResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Code400'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Code403'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/Code404'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Code500'
'503':
description: Service Unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Code503'
/pay/limits:
get:
tags:
- Payins
summary: Get payin authorization limits
description: "\nRetrieves the current authorization level, per-window limits, and volume totals for an\nend-user participant on the authenticated platform. Optionally scoped to a specific\nmerchant when `merchant_participant_code` is supplied (the authenticated platform must\noperate for that merchant).\n "
parameters:
- $ref: '#/components/parameters/Signature'
- $ref: '#/components/parameters/Timestamp'
- name: participant_code
in: query
description: End-user participant code (6-character alphanumeric).
required: true
schema:
type: string
- name: merchant_participant_code
in: query
description: Optional merchant participant code. When supplied, the response also includes `merchant_totals` (volume totals aggregated against that merchant). The authenticated platform must operate for the merchant or the request is rejected.
required: false
schema:
type: string
responses:
'200':
description: Platform and (optionally) merchant payin limits.
content:
application/json:
schema:
$ref: '#/components/schemas/GetPayLimitsResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Code400'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Code403'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/Code404'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Code500'
'503':
description: Service Unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Code503'
/pay/{id}/approval:
delete:
tags:
- Payins
summary: Cancel a pending manual approval
description: "\nCancels a pending or approved manual approval for a Tier 4 payin transaction,\nsetting it to REJECTED state.\n\nUse this endpoint when a platform or shopper wants to withdraw a transaction\nthat is awaiting compliance review. Once cancelled, the transaction cannot\nbe reinstated — a new `POST /pay/rfq` must be issued.\n\nThe `merchant_code` query parameter is optional. When omitted, the\nauthenticated platform code is used as the merchant identifier.\n "
parameters:
- $ref: '#/components/parameters/Signature'
- $ref: '#/components/parameters/Timestamp'
- name: id
in: path
description: Transaction id of the manual approval to cancel.
required: true
schema:
type: string
- name: participant_code
in: query
description: End-user participant (shopper) code whose approval to cancel.
required: true
schema:
type: string
- name: merchant_code
in: query
description: Merchant participant code associated with the approval. Defaults to the authenticated platform code when omitted.
required: false
schema:
type: string
responses:
'204':
description: Approval successfully cancelled.
'400':
description: Approval is already in a terminal state (FINALIZED or REJECTED) and cannot be cancelled (authorizer FAILED_PRECONDITION). The body carries the error message.
content:
application/json:
schema:
type: object
required:
- error
properties:
error:
type: string
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Code403'
'404':
description: No cancellable approval found for the given transaction ID and participant.
content:
application/json:
schema:
type: object
required:
- error
properties:
error:
type: string
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Code500'
'503':
description: Service Unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Code503'
components:
schemas:
PayinsTransactionNotAuthorizedError:
type: object
required:
- error
properties:
error:
type: string
example: Transaction not authorized
description: Generic authorization denial with no remediation. Returned with HTTP 403 — `{"error":"Transaction not authorized"}` when the authorizer rejection has no specific mapping or reports no missing fields, or `{"error":"Transaction not permitted"}` when the participant is not permitted to transact (e.g. geographic restriction) — and with HTTP 422 (`{"error":"Participant is not authorized to transact"}`) when the participant/merchant context precludes authorization.
PostPayRfqByIdBody:
type: object
required:
- participant_code
properties:
participant_code:
type: string
description: The participant code for the shopper whose quote is being refreshed
example: ABCDEF
GetPayLimitsResponse:
type: object
properties:
message:
type: object
properties:
request_id:
type: string
description: The unique identifier echoed from the request.
example: test-request-id
participant_code:
type: string
description: The shopper participant code.
example: SHOPP1
merchant_participant_code:
type: string
description: The merchant participant code.
example: MERCH1
merchant_classification:
type: string
description: Classification of the merchant (e.g. NON-TRANSFERABLE-3).
example: NON-TRANSFERABLE-3
current_level:
type: integer
description: Current authorization level for this participant (1–4).
example: 2
current_limits:
type: array
description: Authorization limits for each time window at the current level.
items:
$ref: '#/components/schemas/PayCurrentLimit'
platform_totals:
type: array
description: Aggregated transaction volume totals across the platform.
items:
$ref: '#/components/schemas/PayVolumeTotal'
merchant_totals:
type: array
description: Aggregated transaction volume totals for the merchant.
items:
$ref: '#/components/schemas/PayVolumeTotal'
PayFeeTierBreakdown:
type: object
description: Breakdown of how the fee was calculated across tiers.
properties:
tier:
type: number
description: The tier number (1-based).
example: 1
amount_in_tier:
type: string
description: The deposit amount that fell into this tier.
example: '100.00'
fee_bps:
type: string
description: The fee rate in basis points applied for this tier.
example: '40'
fee_amount:
type: string
description: The fee amount calculated for this tier.
example: '0.40'
Code503:
type: object
description: 'A downstream dependency was unavailable, timed out, or returned a retryable error. Safe to retry; the response carries a `zh-allow-retry: true` header.'
required:
- error
properties:
error:
type: string
example: Service Unavailable
Code404:
type: object
description: The requested resource does not exist or is not visible to the caller.
required:
- error
properties:
error:
type: string
example: Not Found
PayinsAuthorizationManualApprovalError:
type: object
required:
- error
- transaction_id
properties:
error:
type: string
example: Manual approval required for this transaction
description: 'Human-readable error message. Possible values: "Manual approval required for this transaction" (no approval on file yet), "Approval pending, shopper must wait" (an approval request is already in review), "Manual approval for this transaction was rejected" (the approval was rejected by compliance or the platform), or "Manual approval for this transaction has already been finalized" (the approval was already consumed).'
transaction_id:
type: string
example: txn_01HZF7QWXJ8XK3VYNS6TKDM4G2
description: Identifier of the manual approval review. Persist this and re-use it when checking approval status or cancelling the request.
PayVolumeTotal:
type: object
properties:
window:
type: string
description: The time window for this total.
enum:
- daily
- annual
- lifetime
example: daily
currency:
type: string
description: Currency of the total amount.
example: USD
total_amount:
type: string
description: Total transacted amount in this window, expressed as a decimal string.
example: '5000.00'
transaction_count:
type: integer
description: Number of transactions in this window.
example: 12
PostPayRfqBody:
type: object
required:
- participant_code
- pay_asset
- quoted_total
- quoted_currency
properties:
participant_code:
type: string
description: The participant code for the shopper making the payment
example: ABCDEF
pay_asset:
type: string
description: The cryptocurrency asset to be deposited (e.g., BTC, ETH, USDC)
example: BTC
quoted_total:
type: string
description: The total fiat amount the shopper is paying
example: '100.00'
quoted_currency:
type: string
description: The fiat currency for the payment. Currently only USD is supported
enum:
- USD
example: USD
account_label:
type: string
maxLength: 20
description: Optional account label for the transaction. Defaults to "pay"
example: pay
client_reference_id:
type: string
maxLength: 50
description: An optional client-provided reference ID for tracking purposes
example: order-12345
merchant_participant_code:
type: string
minLength: 1
maxLength: 50
description: Optional participant code of the merchant tied to this transaction. When omitted, the platform itself is treated as the merchant.
example: MERCH01
PayinsAuthorizationKycError:
type: object
required:
- error
- missing_requirements
properties:
error:
type: string
example: Shopper KYC requirements for this transaction are not met
description: Human-readable error message indicating the shopper is missing required KYC data for the requested transaction.
missing_requirements:
type: array
items:
type: string
example:
- first_name
- last_name
- date_of_birth
description: List of participant fields that must be collected or updated for the shopper to pass authorization at the requested amount and merchant MCC tier.
Code403:
type: object
description: Authentication or authorization failed. `error` is always `true`; `message` explains which check failed (missing API key, bad signature, insufficient permission, etc.).
required:
- error
- message
properties:
error:
type: boolean
example: true
message:
type: string
example: This api key does not have write permission to this endpoint
GetPayTransactionsResponse:
type: object
properties:
message:
type: array
description: Array of pay transactions
items:
$ref: '#/components/schemas/PayTransaction'
page:
type: integer
format: int32
description: Current page number (1-indexed)
example: 1
page_size:
type: integer
format: int32
description: Number of items per page (capped at 50)
example: 50
total_pages:
type: integer
format: int32
description: Total number of pages available
example: 1
Code500:
type: object
description: An unexpected error occurred on the server. The request can be retried after a short delay, but the same inputs may reproduce the error.
required:
- error
properties:
error:
type: string
example: Internal Server Error
PayinsAuthorizationLimitError:
type: object
required:
- error
properties:
error:
type: string
example: Shopper's Daily Spend Limit for this type of transaction have been hit.
description: Returned with a 403 status when the shopper has exceeded their daily or lifetime spend aggregate at the current authorization level. Lifting the limit requires manual review.
PayCurrentLimit:
type: object
properties:
limit:
type: string
description: The authorized limit for this window, expressed as a decimal string.
example: '1000.00'
spent:
type: string
description: Amount spent within this window, expressed as a decimal string.
example: '250.00'
remaining:
type: string
description: Remaining capacity within this window, expressed as a decimal string.
example: '750.00'
window:
type: string
description: The time window this limit applies to.
enum:
- daily
- annual
- lifetime
example: daily
PayTransaction:
type: object
properties:
participant_code:
type: string
description: The shopper participant code.
example: ABCDEF
pay_asset:
type: string
description: Cryptocurrency asset being paid with (e.g., BTC, ETH, USDC).
example: BTC
rate:
type: string
description: Conversion rate applied at execution time.
example: '65658.42'
quoted_currency:
type: string
description: Fiat currency for the payment.
example: USD
source_address:
type: string
description: Sender's wallet address that originated the deposit.
example: '0xA32A6aA6a3B87b49BeaB01393e2020C3C191CD61'
deposit_address:
type: string
description: Wallet address where the crypto was sent.
example: bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
quantity:
type: string
description: Crypto quantity deposited.
example: '0.00152307'
notional:
type: string
description: Fiat value of the deposit in `quoted_currency`.
example: '100.00'
success:
type: boolean
description: Whether the payment completed successfully.
example: true
status_reason:
type: string
description: Human-readable failure reason. Empty string if successful.
example: ''
status_reason_code:
type: string
description: 'Machine-readable code describing the outcome of the payment. Examples: `DEPOSIT_PROCESSED`, `AMOUNT_ABOVE_MAXIMUM`, `AMOUNT_BELOW_MINIMUM`.'
example: AMOUNT_ABOVE_MAXIMUM
fund_timestamp:
type: number
description: Unix timestamp in milliseconds indicating when the payment quote was created.
example: 1712756400000
deposit_timestamp:
type: number
description: Unix timestamp in milliseconds indicating when the blockchain deposit was detected.
example: 1712756700000
transaction_id:
type: string
description: On-chain transaction identifier associated with the deposit.
example: '0x408482348dcb0e0331a9148d50f8c76db08138f63fb1586fcfc45200a91ccc5f'
account_label:
type: string
description: The account label that received the payment. Defaults to `pay` for pay transactions.
example: pay
fund_id:
type: string
format: uuid
description: zerohash-generated unique identifier for this payment event.
example: 29e6d7b8-d604-4212-86ee-998fef35505e
is_first_deposit:
type: boolean
description: Indicates whether this was the participant's first deposit (used for first-deposit fee treatment).
example: false
raw_fee_bps:
type: string
description: Initial fee rate expressed in basis points.
example: '50'
deposit_fee_bps:
type: string
description: Effective fee rate in basis points that was actually applied after any minimum floors were considered.
example: '50'
raw_fee_notional:
type: string
description: Initial fee amount calculated directly from `raw_fee_bps`, in `quoted_currency`.
example: '0.50'
deposit_fee_notional:
type: string
description: Final fee amount actually charged to the participant, in `quoted_currency`.
example: '0.50'
deposited_asset:
type: string
description: Actual asset deposited on-chain. May differ from `pay_asset` when cross-chain fallback occurred.
example: BTC
reference_id:
type: string
description: The client-provided reference identifier for this payment, or empty string if none was supplied.
example: order-12345
source:
$ref: '#/components/schemas/PayTransactionSource'
deposit_fee_type:
type: string
description: The fee model applied to this transaction.
enum:
- flat
- tiered
example: flat
fee_tier_breakdown:
type: array
description: Breakdown of how the fee was calculated across tiers. Only present when `deposit_fee_type` is `tiered`.
items:
$ref: '#/components/schemas/PayFeeTierBreakdown'
merchant_participant_code:
type: string
description: The merchant participant code associated with this payment. Only present when a merchant is linked to the transaction.
example: MERCH01
Code400:
type: object
description: Request was rejected by validation or a downstream service. `errors` is an array of human-readable messages; each entry describes a single validation failure or business-rule violation.
required:
- errors
properties:
errors:
type: array
items:
type: string
example:
- body/amount must be >= 0
- body/asset is required
PayTransactionSource:
type: object
description: Source of the auth transaction. Only present if Auth Embedded is enabled for the platform.
properties:
type:
type: string
description: Type of transaction source.
enum:
- CUSTO
# --- truncated at 32 KB (35 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/zero-hash/refs/heads/main/openapi/zero-hash-payins-api-openapi.yml