SDK y API

API REST Reference

Endpoints públicos del backend Accesly. La mayoría de integradores no toca esto directamente porque el SDK envuelve todo. Consulta esta referencia si necesitás llamar al backend desde un lenguaje distinto a JavaScript o desde tu propio servidor.

Base URL

EnvironmentURL
Developmenthttps://api.accesly.xyz
Staginghttps://api-staging.accesly.xyz
Producciónhttps://api.accesly.xyz

Autenticación

La API usa dos mecanismos de auth según el endpoint:

JWT Cognito (usuario end)

La mayoría de endpoints requieren un JWT válido de Cognito en el header Authorization: Bearer <jwt>. El JWT se obtiene del flow de signin.

curl https://api.accesly.xyz/wallets \
  -H "Authorization: Bearer eyJhbGciOi..."

API keys (integrador)

Endpoints de /v1/* son server to server. Se autentican con API key del integrador en el header x-accesly-api-key: sk_live_.... Estas keys se crean en el dashboard.

curl https://api.accesly.xyz/v1/wallets/CBUS... \
  -H "x-accesly-api-key: sk_live_abc123..."

Errores comunes

Todas las responses de error tienen esta shape:

{
  "error": "WalletNotEnrolled",
  "code": "WALLET_NOT_ENROLLED",
  "message": "The wallet is not enrolled for asset USDC.",
  "details": { "asset": "USDC" }
}

Catálogo completo de errores en Errores.

Wallet

GET /wallets

Devuelve la wallet del usuario Cognito autenticado, o null si no tiene.

// Response 200
{
  "walletAddress": "CBUSYQ6AW...",
  "appId": "app_d_prueba1_j42h0",
  "createdAt": "2026-06-29T22:36:20.381Z",
  "onChain": true,
  "deployedTxTargetsCount": 3,
  "gAddress": "GD6EVND7...",
  "bootstrapGComplete": true
}

POST /wallets

Crea una wallet nueva. Body:

{
  "appId": "app_d_...",
  "pubkeyEd25519": "<hex 64>",
  "secp256r1Pubkey": "<hex 130>",
  "emailCommitment": "<hex 64>",
  "fragmentF2": { "ciphertext", "nonce", "algo" },
  "fragmentF3": { "ciphertext", "nonce", "algo" },
  "emailHash": "<hex 64>",
  "recoverySalt": "<base64>"
}

POST /wallets/fragment-2

Descarga F2 envuelto en session key ECDH efímera. Body: { "clientEphemeralPubkey": "<base64 X25519>" }. Response tiene { nonce, ciphertext, authTag, serverEphemeralPubkey }.

GET /wallets/{address}/balance

Balance XLM + USDC + otros trustlines de la wallet. Endpoint público (no requiere auth), útil para explorers públicos.

GET /wallets/{address}/history

Historia de transacciones paginada. Query params: limit, cursor.

Transacciones

POST /wallets/tx/simulate

Backend arma una tx unsigned. Body:

{
  "kind": "transfer",
  "asset": "USDC",
  "destination": "CBXY...",
  "amount": "10.5"
}

Response incluye unsignedXdr, signaturePayloadHashBase64, contextRuleIds, placeholderAuthEntryXdr. El SDK usa esto para armar la firma client side.

POST /wallets/tx/submit

Body: { "unsignedXdr", "signedAuthEntryXdr" }. Backend agrega fee bump con KMS relayer, submitea a Soroban RPC, devuelve { "txHash", "status" }.

Swap

POST /swap/simulate

Cotiza swap XLM ↔ USDC vía Soroswap. Body:

{
  "fromAsset": "XLM",
  "toAsset": "USDC",
  "amountIn": "1000000",
  "slippageBps": 50
}

Response: { quote: { amountOut, priceImpactPct, platform }, unsignedXdr, signaturePayloadHashBase64, ... }.

POST /swap/submit

Submit con firma. Body: { "unsignedXdr", "signedAuthEntryXdr" }.

POST /swap-sdex/simulate + /submit + /finalize

Flow de tres pasos para swap classic SDEX. Requiere G address bootstrappeada. El SDK auto dispara bootstrapG y addTrustlineG si están missing.

Recovery

POST /recovery/otp/request

Body: { "email": "..." }. Rate limited: 1 por 60s, 3 por hora. Response 200 OK aunque el email no exista (anti enumeración).

POST /recovery/otp/verify

Body: { "email", "code", "idToken?" }. Response: { "recoveryJwt": "...", "expiresAt": <epoch ms> }. El idToken es opcional y sirve para atar el JWT al Cognito sub actual.

GET /fragments/3

Header X-Recovery-Jwt: <jwt>. Devuelve { fragmentF3Encrypted, fragmentF2Recovery, recoverySalt }. Requiere JWT válido con scope recovery-finalize.

POST /recovery/simulate-rotate-signer

Header X-Recovery-Jwt: <jwt>. Body:

{
  "newOwnerEd25519Pubkey": "<hex 64>",
  "newSecp256r1Pubkey": "<hex 130>",
  "newEmailCommitment": "<hex 64>"
}

Backend arma la tx rotate_signer y devuelve { unsignedXdr, signaturePayloadHashBase64, placeholderAuthEntryXdr, contextRuleIds }.

POST /recovery/finalize

Header X-Recovery-Jwt: <jwt>. Body incluye el signedAuthEntryXdr + los nuevos fragments. Backend ejecuta el rotate on chain y persiste los fragments nuevos.

En wallets con 4 o más context rules el submit rebota con HTTP 422 code: ROTATE_SIGNER_WRITE_CAP_EXCEEDED. El body del error incluye rotatableRuleIds con los IDs elegibles para partition. El SDK reacciona automáticamente y ejecuta el flow multi tx descrito abajo.

POST /recovery/simulate-rotate-partial

Header X-Recovery-Jwt: <jwt>. Body:

{
  "partialRuleIds": [0, 1],
  "newOwnerEd25519Pubkey": "<hex 64>",
  "newSecp256r1Pubkey": "<hex 130>"
}

Backend arma la tx rotate_signer_partial(rule_ids, new_owner, new_secp) del Smart Account v3.2.0 para rotar solo el subset de rules pasado. Response idéntico al simulate legacy: { unsignedXdr, signaturePayloadHashBase64, placeholderAuthEntryXdr, contextRuleIds }.

POST /recovery/rotate-partial

Header X-Recovery-Jwt: <jwt>. Body { unsignedXdr, signedAuthEntryXdr, partialRuleIds }. Backend re simula con la auth firmada, submitea el batch y devuelve { walletAddress, txHash, status, rotatedRuleIds }. No persiste ningún fragment. El SDK llama este endpoint una vez por batch.

POST /recovery/simulate-finalize-rotation

Header X-Recovery-Jwt: <jwt>. Body:

{
  "newOwnerEd25519Pubkey": "<hex 64>",
  "newSecp256r1Pubkey": "<hex 130>",
  "newEmailCommitment": "<hex 64>"
}

Backend arma la tx finalize_rotation(new_owner, new_secp, new_email_commitment). Se llama una sola vez cuando todos los batches de rotate-partial ya fueron aplicados.

POST /recovery/finalize-rotation

Header X-Recovery-Jwt: <jwt>. Body incluye signedAuthEntryXdr + los mismos fragments que /recovery/finalize (F1', F2', F2 recovery', F3', salt, email commitment). Backend ejecuta el finalize_rotation on chain y persiste TODOS los nuevos fragments en la misma llamada. Response idéntico a /recovery/finalize: { walletAddress, txHash, status }.

KYC y fiat

POST /kyc/register-bank-account

Registra CLABE + datos personales para KYC en Dynerox. Body:

{
  "clabe": "012180011201000123",
  "firstName": "Luis",
  "paternalLastName": "Perez",
  "maternalLastName": "Sanchez",
  "holderCurp": "PESL800101HDFRRS01",
  "birthDate": "1980-01-01",
  "phone": "+525512345678"
}

GET /kyc/status

Devuelve el status KYC del usuario actual. Response: { "status": "active", "authorizationUrl": "https://auth.dynerox.com/..." }.

POST /onramp

Crea route Dynerox de MXN a USDC Stellar. Body:

{
  "amount": "500",
  "currency": "MXN"
}

Response: { "routeId", "status", "authorizationUrl" }. El user visita el authorizationUrl para completar el pago SPEI.

POST /offramp

Body: { "amount", "asset": "USDC", "bankAccountId" }. Retira USDC de la wallet a la CLABE registrada.

App config

GET /app-config/{appId}

Público. Devuelve el appConfig del app. Usado por el SDK al mount del Provider.

{
  "appId": "app_d_...",
  "appName": "Mi App",
  "network": "testnet",
  "branding": { ... },
  "auth": { "providers": ["email", "google"] },
  "trustlines": [...],
  "cognito": { "userPoolId", "clientId" }
}

PATCH /apps/{appId}

Editar el appConfig. Requiere JWT del developer pool y ownership del app. Usado por el dashboard.

Handles y contactos

POST /handles

Reserva un handle global tipo @luis.accesly. Body: { "handle": "luis" }. FCFS: primer usuario que lo reserve gana.

GET /handles/{handle}

Resuelve un handle a la wallet address. Público.

GET /contacts

Address book del usuario actual. Response array de { contactId, name, walletAddress, handle? }.

POST /contacts

Agrega contacto. Body: { "name", "walletAddress" o "handle" }.

Metadata y observabilidad

Idempotencia

Operaciones que mueven state aceptan header Idempotency-Key: <uuid>. Si repetís el mismo key en 24h, devolvemos la response cached en vez de re ejecutar. Recomendado para createWallet, recovery/finalize y tx/submit.

Rate limits

Endpoint groupLímite
Recovery OTP request1 por 60s, 3 por hora por email
Recovery OTP verify5 intentos por OTP
Auth signIn10 por minuto por IP
Tx submit60 por minuto por user
Balance / history reads300 por minuto por user

Response 429 cuando se supera el límite. Header Retry-After indica segundos hasta reintentar.

Siguientes pasos