HTTP integration examples
XferAPI is a hosted JSON API. Use the HTTP client already present in your backend; no embedded engine or public SDK is required.
Base URL
https://api.xferapi.com/v1Authentication
Bearer xfer_sk_<key_id>_<secret>Content type
application/jsonPayments and business events
Call XferAPI after your provider confirms the event
XferAPI does not authorize card or fiat payments. Keep Stripe, Alipay, WeChat Pay, or another provider in its existing role. After your backend verifies a payment, renewal, refund, reward, or usage event, submit one idempotent XferAPI transfer to move the corresponding customer-defined units.
Provider confirms
Verify the signed webhook or authoritative server response.
Persist intent
Save one transfer ID, scene, and complete payload with the business event.
Create transfer
Issue, spend, split, or reverse units through the ledger-scoped API.
Inspect result
Use the response code, request ID, Transfer, Records, and Console history.
curl "https://api.xferapi.com/v1/accounts/member_42" \
-H "Authorization: Bearer $XFERAPI_KEY"Production-safe write pattern
Create a transfer without guessing after a timeout
Save the business transfer ID and complete payload before the first request. Business outcomes use HTTP 200, while protocol and infrastructure failures may use a non-2xx status; always parse a valid JSON envelope and branch on its string code. Only ok is success. A timeout, connection failure, invalid or missing envelope, internal_error, service_unavailable, or transfer_state_conflict is uncertain. Resend the exact original ID, scene, and payload directly; do not generate a new ID or require a read request before retrying.
rate_limit_exceeded, quota_exceeded, and billing_suspended reject the new attempt before it executes. After backoff or after the Workspace condition is resolved, resend the same original request. For POST /v1/transfers, the terminal transaction failure codes are exactly insufficient_balance and execution-time amount_out_of_range. That ID-and-scene pair can never later succeed; fix the business condition, then start a new attempt with a new transfer_id. Handle transfer_already_reversed separately because the pair is already closed, and idempotency_conflict because the pair belongs to another payload; either case requires a new ID for the request you are trying to submit.
Persist identity
Store one transfer ID with the business operation before calling XferAPI.
Keep integers exact
Send every amount as a positive decimal JSON string.
Retry uncertainty
Resend the exact original request directly; a read is not a prerequisite.
Retain request ID
Log X-Request-ID with your own operation ID, without logging the API key.
const transferId = 'order_123' // Persist with your order before the first request.
const scene = 'spend'
const payload = {
transfer_id: transferId,
scene,
background_completion: true,
from_accounts: [
{ account_id: 'buyer_42', asset: 'points', action: 'spend', amount: '100' }
],
to_accounts: [
{ account_id: 'seller_7', asset: 'points', action: 'spend', amount: '90' },
{ account_id: '-2', asset: 'points', action: 'spend', amount: '10' }
]
}
const UNCERTAIN_CODES = new Set([
'internal_error',
'service_unavailable',
'transfer_state_conflict'
])
const RETRY_AFTER_CONDITION_CODES = new Set([
'rate_limit_exceeded',
'quota_exceeded',
'billing_suspended'
])
const TERMINAL_TRANSFER_CODES = new Set([
'insufficient_balance',
'amount_out_of_range'
])
class XferAPIError extends Error {
constructor(message, details) {
super(message)
Object.assign(this, details)
}
}
async function callXferAPI(path, options = {}) {
let response
try {
response = await fetch(`https://api.xferapi.com/v1${path}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.XFERAPI_KEY}`,
'Content-Type': 'application/json',
...options.headers
},
signal: AbortSignal.timeout(40_000)
})
} catch (cause) {
throw new XferAPIError('XferAPI response is uncertain', { cause, uncertain: true })
}
const requestId = response.headers.get('x-request-id')
const body = await response.json().catch(() => null)
if (requestId) console.info('XferAPI response request ID:', requestId)
if (!body || typeof body !== 'object' ||
typeof body.code !== 'string' || !('data' in body)) {
throw new XferAPIError('XferAPI returned an invalid transport response', {
httpStatus: response.status,
code: body?.code,
requestId,
uncertain: true
})
}
if (body.code !== 'ok') {
throw new XferAPIError(body.message || body.code, {
httpStatus: response.status,
code: body.code,
requestId,
uncertain: UNCERTAIN_CODES.has(body.code),
retryAfterCondition: RETRY_AFTER_CONDITION_CODES.has(body.code),
terminalTransfer: TERMINAL_TRANSFER_CODES.has(body.code),
pairClosed: body.code === 'transfer_already_reversed',
payloadConflict: body.code === 'idempotency_conflict'
})
}
if (!response.ok) {
throw new XferAPIError('XferAPI returned ok with a non-success HTTP status', {
httpStatus: response.status,
code: body.code,
requestId,
uncertain: true
})
}
return { data: body.data, requestId }
}
const create = () => callXferAPI('/transfers', {
method: 'POST',
body: JSON.stringify(payload)
})
async function submitSafely() {
try {
return await create()
} catch (error) {
if (!error.uncertain) throw error
// Do not create a new ID or make a read request first. Resend the exact
// original ID, scene, and payload; XferAPI continues the same operation.
return create()
}
}
const result = await submitSafely()
console.info('XferAPI request ID:', result.requestId)import os
import requests
TRANSFER_ID = "order_123" # Persist with your order before the first request.
SCENE = "spend"
PAYLOAD = {
"transfer_id": TRANSFER_ID,
"scene": SCENE,
"background_completion": True,
"from_accounts": [
{"account_id": "buyer_42", "asset": "points", "action": "spend", "amount": "100"}
],
"to_accounts": [
{"account_id": "seller_7", "asset": "points", "action": "spend", "amount": "90"},
{"account_id": "-2", "asset": "points", "action": "spend", "amount": "10"},
],
}
UNCERTAIN_CODES = {
"internal_error",
"service_unavailable",
"transfer_state_conflict",
}
RETRY_AFTER_CONDITION_CODES = {
"rate_limit_exceeded",
"quota_exceeded",
"billing_suspended",
}
TERMINAL_TRANSFER_CODES = {
"insufficient_balance",
"amount_out_of_range",
}
class XferAPIError(Exception):
def __init__(
self,
message,
*,
status=None,
code=None,
request_id=None,
uncertain=False,
retry_after_condition=False,
terminal_transfer=False,
pair_closed=False,
payload_conflict=False,
):
super().__init__(message)
self.status = status
self.code = code
self.request_id = request_id
self.uncertain = uncertain
self.retry_after_condition = retry_after_condition
self.terminal_transfer = terminal_transfer
self.pair_closed = pair_closed
self.payload_conflict = payload_conflict
def call_xferapi(method, path, *, json=None):
try:
response = requests.request(
method,
f"https://api.xferapi.com/v1{path}",
headers={"Authorization": f"Bearer {os.environ['XFERAPI_KEY']}"},
json=json,
timeout=40,
)
except requests.RequestException as error:
raise XferAPIError("XferAPI response is uncertain", uncertain=True) from error
request_id = response.headers.get("X-Request-ID")
try:
body = response.json()
except ValueError:
body = None
if request_id:
print("XferAPI response request ID:", request_id)
if (
not isinstance(body, dict)
or not isinstance(body.get("code"), str)
or "data" not in body
):
raise XferAPIError(
"XferAPI returned an invalid transport response",
status=response.status_code,
code=body.get("code") if isinstance(body, dict) else None,
request_id=request_id,
uncertain=True,
)
if body["code"] != "ok":
raise XferAPIError(
body.get("message") or body["code"],
status=response.status_code,
code=body["code"],
request_id=request_id,
uncertain=body["code"] in UNCERTAIN_CODES,
retry_after_condition=body["code"] in RETRY_AFTER_CONDITION_CODES,
terminal_transfer=body["code"] in TERMINAL_TRANSFER_CODES,
pair_closed=body["code"] == "transfer_already_reversed",
payload_conflict=body["code"] == "idempotency_conflict",
)
if not response.ok:
raise XferAPIError(
"XferAPI returned ok with a non-success HTTP status",
status=response.status_code,
code=body["code"],
request_id=request_id,
uncertain=True,
)
return body["data"], request_id
def create_transfer():
return call_xferapi("POST", "/transfers", json=PAYLOAD)
def submit_safely():
try:
return create_transfer()
except XferAPIError as error:
if not error.uncertain:
raise
# Do not create a new ID or make a read request first. Resend the exact
# original ID, scene, and payload; XferAPI continues the same operation.
return create_transfer()
data, request_id = submit_safely()
print("XferAPI request ID:", request_id)Opt in to an ordinary account overdraft deliberately
Ordinary source accounts require sufficient available balance by default. Add allow_overdraft: true to a from_accounts item only when your product permits that account to become negative, and keep the value unchanged on every retry. Source items for the same ordinary account and asset must agree. Platform accounts always allow overdraft; the field is ignored on destination items.
Keep credentials and exact integers on the server
Do not ship a service key in a browser or mobile app. Amounts use decimal JSON strings across the complete signed 64-bit range; JavaScript backends can parse returned amounts with BigInt without losing precision.
Distinguish a retry from a new attempt
Uncertain results and rate_limit_exceeded, quota_exceeded, or billing_suspended keep the original ID, scene, and exact payload. After insufficient_balance or execution-time amount_out_of_range, that pair can never succeed; the corrected attempt must use a new transfer_id.
Use the API reference for the complete request model, status values, and error contract.