V-RECReceive. Use. Connect.
V-REC DEVELOPERS

V-REC API / Current implementation

The current V-REC platform is split into four API layers: Merchant Server API, Corporate / Partner Payout API, authenticated User Session API, and the internal SAKURA Connected Apps integration. User provisioning, KYC, cards, payout creation and Tenant Funding are intentionally separated by trust boundary.

Two server API credentials
# Merchant Server API
X-VREC-Merchant-Key: vmk_...
X-VREC-Timestamp: ...
X-VREC-Nonce: ...
X-VREC-Signature: ...

# Corporate / Partner Payout API
X-VREC-Key: vrec_...
X-VREC-Timestamp: ...
X-VREC-Nonce: ...
X-VREC-Signature: ...
GETTING STARTED

Configure the API key and webhook first

Before using the Merchant Server API, create an API credential in Merchant Portal, then configure a webhook URL and webhook secret. The API secret and webhook secret are different credentials.

STEP 1

Create an API credential

Merchant Portal → Merchant API

After creation, the Key ID (vmk_...) and secret are shown. The secret is shown once only, so store it in a secure secret manager.

STEP 2

Configure the webhook

Merchant Portal → Merchant API → Merchant Webhook

Register an HTTPS receiver URL and a webhook secret of at least 32 characters. After saving, verify the CONFIGURED status and the stored URL.

STEP 3

Verify with a test delivery

webhook.test

Press “Send test webhook” in Merchant Portal and confirm DELIVERED with an HTTP 2xx response in the delivery history. Delivery is processed by the scheduler.

API authentication headers

X-VREC-Merchant-Key: vmk_...
X-VREC-Timestamp: UNIX timestamp
X-VREC-Nonce: unique random string
X-VREC-Signature: HMAC-SHA256(...)

Webhook receiver headers

X-VREC-Event: user.registered | user.kyc.updated | tenant.funding.* | webhook.test
X-VREC-Delivery: mwd_...
X-VREC-Timestamp: UNIX timestamp
X-VREC-Signature: HMAC-SHA256(timestamp + "\n" + delivery + "\n" + event + "\n" + SHA256(raw_body))

Verify the signature against the unmodified raw body and return HTTP 2xx when the event is accepted.

API SURFACES

Four trust boundaries

Do not mix corporate backend APIs, cardholder session APIs and the internal V-REC ↔ SAKURA integration.

1. Merchant Server API

/api/merchant/v1/*

Provision users, manage status, monitor KYC, read masked cards, Merchant-attributed balances/transactions/payouts and reports.

2. Corporate / Partner Payout API

/api/v1/payouts*

Creates and reads corporate payout instructions to V-REC beneficiaries. Creating a payout does not itself credit funds.

3. User Session API

/api/v1/me/*

Authenticated cardholder KYC, card issuance/actions, personal balances/transactions and step-up sensitive-card display.

4. SAKURA Connected Apps

/api/integrations/vrec/*

Internal V-REC ↔ SAKURA integration for profile/KYC, cards and Tenant Funding status/balance/fund/return/usage. This is not a direct public Merchant API.

v2.5.0: the crypto wallet (crypto_wallet / crypto_wallet_manage) uses the same channel.

ONLINE LEARNING / GOOGLE CALENDAR / ERC-20

Online learning and custom token APIs

Merchant-scoped lessons and progress, external completion evidence, approved Google Calendar sync, dedicated pool history and gasless payout requests. Call through a signed merchant backend, not in-browser HMAC.

Open the 12-endpoint web reference · Integration flow · OpenAPI YAML

MERCHANT AUTH

Merchant credentials define the Merchant boundary

The Merchant API is server-to-server only. The Merchant boundary is derived from the credential and never from merchant_id in the request body.

HMAC-SHA256±300s TimestampReplay ProtectionOptional IP WhitelistIdempotency
METHOD + "\n" +
PATH + "\n" +
CANONICAL_QUERY + "\n" +
TIMESTAMP + "\n" +
NONCE + "\n" +
SHA256(raw_body)
MERCHANT API

User, KYC, card and read APIs

GET/api/merchant/v1/context

Authenticated Merchant context and available scopes

GET/api/merchant/v1/profile-options

SAKURA-owned country / dial-code catalog

GET/api/merchant/v1/users

List V-REC users belonging to this Merchant

POST/api/merchant/v1/users

Create a user. Idempotency-Key required

POST/api/merchant/v1/user-links

Generate a user-consent URL for an existing SAKURA user

GET/api/merchant/v1/user-links/{id}

Check existing-user consent-link status

GET/api/merchant/v1/users/{id}

User detail

POST/api/merchant/v1/users/{id}/status

ACTIVE / SUSPENDED

GET/api/merchant/v1/users/{id}/kyc

Synchronize and read current SAKURA KYC state

POST/api/merchant/v1/users/{id}/kyc/start

Require the user to continue in V-REC. No eKYC URL is returned to the Merchant

GET/api/merchant/v1/crypto-sales

v2.6.0 Crypto sales and royalties (crypto_sales.read)

GET/api/merchant/v1/users/{id}/wallet

v2.5.0 read-only wallet address and balances (wallets.read)

GET/api/merchant/v1/users/{id}/cards

Masked cards only; never PAN/CVV

POST/api/merchant/v1/users/{id}/cards

Issue VIRTUAL cards (operator-granted cards.issue, Idempotency-Key required)

GET/api/merchant/v1/users/{id}/cards/issuance/{key}

Inspect issuance result; uncertain outcomes require operator reconciliation, not reissuance

GET/api/merchant/v1/users/{id}/cards/{cardId}/transactions

MatchMove issuer card transactions (transactions.read)

POST/api/merchant/v1/users/{id}/cards/{cardId}/status

Explicit cards.manage grant allows FREEZE/SUSPEND (issuer FROZEN) or UNFREEZE. Permanent CLOSE is not supported.

POST/api/merchant/v1/users/{id}/cards/{cardId}/sensitive-link

Issue hosted one-time cardholder password form URL; Merchant never receives PAN/CVV (operator-granted cards.sensitive.link)

GET/api/merchant/v1/users/{id}/matchmove-balance

Issuer MatchMove live balance via SAKURA, separate from ledger; fails 503 when unavailable

POST/api/merchant/v1/users/{id}/cards/physical/link

Link a pre-issued physical card by Proxy / Association ID

GET/api/merchant/v1/users/balances

balances.read · Bulk Merchant-attributed balances of your users (up to 200 per call)

GET/api/merchant/v1/users/{id}/balance

Merchant-attributed balance

GET/api/merchant/v1/users/{id}/transactions

Merchant-attributed transactions

GET/api/merchant/v1/payouts

Read payouts associated with this Merchant

GET/api/merchant/v1/report

Merchant summary report

Two independent onboarding emails on user creation

POST /api/merchant/v1/users automatically sends the applicable V-REC password email and SAKURA password email by default. Set notifications.send_vrec_activation_email and notifications.send_sakura_password_setup_email as JSON booleans to control the two emails independently.

"notifications": {
  "send_vrec_activation_email": false,
  "send_sakura_password_setup_email": true
}

The V-REC email is applicable only to a newly created local login. The SAKURA email is applicable only when SAKURA returns a one-time setup URL. The SAKURA setup URL is not exposed through the Merchant API. Check user.notifications in the response for sent / disabled / not_applicable / failed.

Full create-user / notification reference →

CORPORATE PAYOUT API

Create a payout instruction for a user

The Corporate / Partner Payout API moves MatchMove USD from the Merchant bound to an approved Partner to a V-REC beneficiary's MatchMove USD wallet. A Tenant that directly allocates funds uses the separate Merchant Tenant Funding API. Payouts created through this API are never funded by Tenant Funding.

Funding requires verified settlement Reference-only LEGACY_REFERENCE payouts cannot be created; new payouts require MERCHANT_BALANCE. A payout becomes CREDITED only when SAKURA returns COMPLETED for the same payout ID, beneficiary, asset and amount with a MatchMove transaction ID and SAKURA transfer UUID. Transfer evidence is stored before the ledger lot is created; if only the lot creation is deferred the payout stays FUNDED and /reconcile retries it. On timeouts, never resubmit; reconcile the existing payout.

POST/api/v1/payouts

Create a payout instruction (no money moves). Idempotency-Key required. PENDING_FUNDS when SAKURA KYC is approved, otherwise AWAITING_KYC. A replay with the same key returns the original payout (201) only if every field is identical.

POST/api/v1/payouts/{id}/fund

Ask SAKURA to transfer MATCHMOVEUSD→MATCHMOVEUSD. Body is a signed {}. HTTP 200 is not success by itself; read status and merchant_transfer_status. Repeating it for the same payout is safe and never sends twice. May take up to 150 seconds.

POST/api/v1/payouts/{id}/reconcile

Read-only reconciliation (never resends). Body is {}. Use for uncertain, in-progress or credit-deferred payouts.

POST/api/v1/payouts/{id}/cancel

Cancel only when SAKURA confirms no transfer can have happened. Body is {}. The external_reference stays reserved after cancellation.

GET/api/v1/payouts/{id}/approval-status

Read the Merchant over-limit approval state from SAKURA. Call /fund again for the same payout only when fund_retry_permitted=true.

GET/api/v1/payouts

List Partner payouts (newest 100, no paging)

GET/api/v1/payouts/{id}

Get payout (stored V-REC state; does not call SAKURA)

GET/api/v1/beneficiaries/{id}/status

Check beneficiary status/KYC

POST/api/v1/webhooks/test

Verify Partner API signing (echoes your JSON; does not send a webhook)

Important: payout creation ≠ balance credit

POST /api/v1/payouts only records an instruction. Money moves through /fund, and the payout becomes CREDITED once SAKURA transfer evidence is verified. Tenant Funding only funds historical LEGACY_REFERENCE payouts.

PARTNER HMAC

Partner API request signing

The Partner API canonical string differs from the Merchant API (different order, no query). Do not reuse the Merchant API implementation.

X-VREC-Key: vrec_...            # Partner key ID
X-VREC-Timestamp: 1792540800    # Unix seconds, within ±300 s
X-VREC-Nonce: 7d1c...           # unique, 1-128 chars, never reused
X-VREC-Signature: hex HMAC-SHA256

body_hash = hex(SHA256(raw_body))          # GET: SHA256("") = e3b0c442...b855
message   = TIMESTAMP + "\n" + NONCE + "\n" + METHOD + "\n" + PATH + "\n" + body_hash
signature = hex(HMAC-SHA256(message, partner_secret))
# PATH = URL path only, e.g. /api/v1/payouts/po_abc/fund

Every POST requires a JSON object body; send {} when there are no fields (an empty body returns 400 invalid_json). Authentication errors are 401 (partner_not_authorized / stale_timestamp / invalid_nonce / invalid_signature), a reused nonce is 409 replayed_nonce, and a non-allow-listed IP is 403 ip_not_allowed.

CORPORATE PAYOUT · MERCHANT BALANCE

From instruction to credit

Amounts are in USD. Both source_asset and destination_asset are required and must match. Only MATCHMOVEUSD-to-MATCHMOVEUSD can be funded; USD-to-USD and SGP-USD-to-SGP-USD can be created but are rejected by /fund. Cross-asset conversion is prohibited.

POST /api/v1/payouts
Idempotency-Key: corp-20261021-0001
{
  "beneficiary_id": "VREC-...",
  "merchant_id": "mrc_...",            // optional, see OpenAPI
  "amount": "100.00",                  // string, max 2 decimals
  "currency": "USD",
  "funding_mode": "MERCHANT_BALANCE",
  "source_asset": "MATCHMOVEUSD",
  "destination_asset": "MATCHMOVEUSD",
  "external_reference": "INVOICE-2026-10-001",   // required, unique per Partner
  "purpose": "corporate_payout"                  // optional, max 80 chars
}
→ 201 {"id":"po_...","status":"PENDING_FUNDS","merchant_transfer_status":null, ...}

POST /api/v1/payouts/po_.../fund        body: {}
→ 200 {"status":"CREDITED","merchant_transfer_status":"COMPLETED","provider_tx_id":"...", ...}
merchant_transfer_statusWhat to do
COMPLETED + CREDITEDDone
COMPLETED + FUNDEDTransfer confirmed; ledger credit deferred (e.g. KYC). Call /reconcile later
SUBMITTINGIn progress. Check with GET or /reconcile
RECONCILE_REQUIREDOutcome uncertain. Use /reconcile only; never create a new payout
RETRYABLEProven not sent. Check merchant_pre_submission_reject_code, fix the cause, then /fund the same payout again or /cancel
FAILED_CONFIRMEDOperations confirmed non-execution. /cancel, then create a new payout with a new external_reference

Prerequisites: an approved Partner bound to the Merchant, an APPROVED and ACTIVE/FULL beneficiary, the corporate_payout Connected App scope, and a Merchant-enabled payout policy (per-payout and daily limit, UTC day) in SAKURA Merchant console → “V-REC Corporate Payout”. Without it /fund returns RETRYABLE (corporate_payout_policy_not_enabled).

Over a limit: /fund returns RETRYABLE (corporate_payout_merchant_approval_required) and SAKURA queues an approval request. After the Merchant approves it in SAKURA (valid 24 hours), confirm fund_retry_permitted=true via GET /approval-status, then call /fund for the same payout. If it is not approved you can /cancel.

Full parameters, responses and error codes: Payout OpenAPI →

TENANT FUNDING · v2.4.2

SAKURA remains authoritative; V-REC exposes guarded Merchant operations

V-REC does not maintain an independent authoritative Tenant available balance. Use balance/capacity to read SAKURA-authoritative state, then execute either direct allocation or payout-based funding. New money movement is allowed only for an ACTIVE/FULL membership whose source Merchant equals the Tenant Merchant. READONLY may still read masked card metadata, but cannot receive new Tenant funds.

Current funding requires more than the V-REC tenant_funding.read/write HMAC permission: existing SAKURA Connected Apps must be explicitly reauthorized for tenant_funding and checked for exact Merchant/origin/program binding, ENFORCED, api_execute_enabled and reconciliation state. Missing grants or READONLY membership block new allocations.

GET/api/merchant/v1/tenant-funding/balance

tenant_funding.read · SAKURA-authoritative available / reserved / expiry policy

GET/api/merchant/v1/tenant-funding/diagnostics

tenant_funding.read · Read-only verification of schema, binding, SAKURA, membership, source/target Merchant and operation readiness

GET/api/merchant/v1/tenant-funding/users/{id}/capacity

tenant_funding.read · Preflight membership eligibility and allocatable amount (tenant available − allocation fee)

POST/api/merchant/v1/tenant-funding/allocations

tenant_funding.write · Direct Tenant→User allocation. Idempotency-Key required. 409 + REJECTED when not executed (e.g. insufficient balance, or another operation in progress: operation_in_progress / unresolved_request_exists). REJECTED moved no money; fix the cause and retry with a new Idempotency-Key.

GET/api/merchant/v1/tenant-funding/allocations

tenant_funding.read · List allocations updated since a given time (for cron result collection)

GET/api/merchant/v1/tenant-funding/allocations/{id}

tenant_funding.read · Read allocation operation status

POST/api/merchant/v1/tenant-funding/allocations/{id}/reverse

tenant_funding.write · Return only unspent principal from that allocation

POST/api/merchant/v1/tenant-funding/users/{id}/return

tenant_funding.write · Return user-held principal originating from this Tenant

Direct allocation
GET  /tenant-funding/balance
GET  /tenant-funding/users/{id}/capacity
        ↓ eligible / available
POST /tenant-funding/allocations + Idempotency-Key
        ↓
V-REC re-checks Membership + SAKURA status
        ↓
SAKURA /api/integrations/vrec/tenant-funding/fund
        ↓ 201 COMPLETED / 409 REJECTED (not executed) / 202 UNKNOWN (retry same key)
GET  /tenant-funding/allocations?updated_since=…  (cron result collection)
V-REC creates provenance lot with expiry snapshot

Payout-based funding (historical LEGACY_REFERENCE payouts only)
existing LEGACY_REFERENCE payout, KYC APPROVED
        ↓ tenant-funding cron, same SAKURA fund guard
        ↓ COMPLETED
Payout = CREDITED
# New payouts (MERCHANT_BALANCE) are never funded by Tenant Funding.

Expiry (including a 180-day policy where applicable) is stored on the V-REC lot from the effective SAKURA snapshot returned at allocation execution. Viewing, spending or returning the lot does not restart the expiry period.

SAKURA /api/integrations/vrec/tenant-funding/* remains an internal Connected Apps API. External Merchants must use /api/merchant/v1/tenant-funding/* instead of calling the SAKURA integration endpoints directly.

USER SESSION API

Cardholder-only operations

GET/api/v1/me

Authenticated user context

GET/api/v1/me/kyc

Own KYC state

GET/api/v1/me/kyc/form

Fetch current dynamic SAKURA KYCFORM

POST/api/v1/me/kyc/submit

Submit KYC and follow next_action

POST/api/v1/me/cards/VIRTUAL

Issue a virtual card after KYC approval

POST/api/v1/me/cards/PHYSICAL

Issue a physical card after KYC approval

POST/api/v1/me/cards/physical/link

Cardholder step-up to link a pre-issued physical card

POST/api/v1/me/cards/{id}/sensitive

Temporary PAN/expiry/CVV after current-password step-up; no-store

GET/api/v1/me/wallet

v2.5.0 wallet summary, USDT/ETH balances, gasless eligibility

POST/api/v1/me/wallet

Create an unhosted wallet after step-up; seed phrase returned once (no-store)

POST/api/v1/me/wallet/rotate

Rotate the wallet after step-up

POST/api/v1/me/wallet/purchase-quotes

v2.6.0 Quote a purchase of SGP-USD / merchant points paid in crypto (sale contract)

POST/api/v1/me/wallet/purchases/{contract}/confirm

Accept terms and pay (company 1% + merchant royalty)

POST/api/v1/me/wallet/gasless-transfer

USDT gasless transfer; Idempotency-Key required, gas paid by SAKURA

GET/api/v1/me/balance

User V-REC balance

GET/api/v1/me/transactions

User transactions

GET/api/v1/me/payouts

Payouts received by the user

WORKFLOW SUMMARY

Recommended implementation order

1. Provision user

profile-options → POST /merchant/v1/users

2. KYC

kyc/start → user login → /me/kyc/form → submit → webhook

3. Create payout

beneficiary/status → POST /api/v1/payouts (MERCHANT_BALANCE)

4. Fund

POST /api/v1/payouts/{id}/fund → (approval-status) → reconcile

Tenant allocations use a separate API: /api/merchant/v1/tenant-funding/allocations

5. Cards

KYC APPROVED → /me/cards/VIRTUAL|PHYSICAL

6. Reconciliation

GET payout / Merchant balance / user balance / webhooks

Implementation Workflows →

WEBHOOKS

Asynchronous state changes

user.kyc.updated

Signed Merchant notification carrying the latest KYC state re-read from SAKURA

payout.created / payout.credited

Partner payout notifications (these two only; cancellation and reconciliation states are not notified). There is no event ID: deduplicate on X-VREC-Event + payout.id and confirm state with GET. Signature contract →

Keep webhook secrets server-side and verify signatures against the raw request body.

CURRENT BOUNDARIES

Current public API boundaries

There is no direct Merchant fund endpoint. User payout instructions are created through the Partner Payout API. Operator-granted Merchant credentials may issue VIRTUAL cards, but do not receive PAN/CVV. Cardholders must enter their V-REC password into a short-lived one-time hosted V-REC page to view sensitive details. An explicitly granted cards.manage Merchant credential can freeze/unfreeze cards, but cannot access PIN or PAN/CVV.