# De tu clave al primer dato.

Guía completa de RPG Connecta: claves API, sandbox, consentimiento bancario, datos, webhooks y producción. Documentación pública para personas y agentes.

API: https://www.rpgcapital.com.br/api/connecta/v1
Console: https://www.rpgcapital.com.br/developers
OpenAPI: https://www.rpgcapital.com.br/developers/connecta-openapi.json

## De la cuenta a la primera respuesta

Inicia sesión con Google en la consola. Selecciona Sandbox, crea una clave con nombre y guarda inmediatamente su valor completo: solo se muestra una vez. No necesitas registrar una aplicación, indicar la URL de un sitio ni tener cuenta en Malvo.

Guarda la clave como RPG_API_KEY en tu backend. Crea una conexión ficticia, conserva data.id como RPG_CONNECTION_ID y consulta cuentas y transacciones. Los ejemplos se ejecutan fuera de la consola; las consultas dentro de ella usan tu sesión autenticada.

- 1. Crea una clave RPG de sandbox.
- 2. Ejecuta un ejemplo y guarda el ID de conexión.
- 3. Explora JSON, cambia escenarios y revisa eventos.

```bash
# Requires bash, curl and jq. Store your sandbox key in RPG_API_KEY.
set -euo pipefail
: "${RPG_API_KEY:?Set RPG_API_KEY in your shell}"
BASE='https://www.rpgcapital.com.br/api/connecta/v1'
CONNECTION_ID=$(curl --fail-with-body -sS "$BASE/connections" \
  -H "Authorization: Bearer $RPG_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"provider":"rpg_simulator","scenario":"success"}' | jq -er '.data.id')
curl --fail-with-body -sS "$BASE/connections/$CONNECTION_ID/accounts" \
  -H "Authorization: Bearer $RPG_API_KEY"
curl --fail-with-body -sS "$BASE/connections/$CONNECTION_ID/transactions?offset=0" \
  -H "Authorization: Bearer $RPG_API_KEY"
```

## Claves, permisos y entornos

Envía Authorization: Bearer seguido de tu clave RPG en cada llamada. rpg_test_ identifica sandbox; rpg_live_ identifica producción. El encabezado opcional X-RPG-Environment debe coincidir con la clave. Cada credencial solo accede a las conexiones de su propietario en su entorno.

Guarda secretos en variables del servidor o un gestor de secretos, nunca en el navegador, prompts públicos, registros o Git. Puedes cambiar el nombre en la consola. Para rotar: crea una sustituta, actualiza tu backend y revoca la anterior. La revocación es inmediata.

- Máximo: 10 claves activas por usuario y entorno.
- Máximo: 60 solicitudes por minuto por clave. Respeta Retry-After en HTTP 429.
- Máximo: 20 conexiones activas por usuario y entorno.

## Un sandbox completo dentro de tu espacio

El simulador RPG conserva cuentas y transacciones ficticias por conexión. Usa provider=rpg_simulator y POST /connections/{id}/simulate con un escenario. No contacta con bancos, no exige tarjeta y no realiza pagos reales.

success genera datos completos; pending espera autorización; expired requiere renovar consentimiento; unavailable simula una caída bancaria; partial presenta un snapshot incompleto. Los datos válidos anteriores pueden conservarse durante una caída o espera: revisa status y last_synced_at.

El sandbox dedicado de Malvo es otro adaptador que requiere credenciales de sandbox configuradas por RPG. Su disponibilidad aparece en opciones avanzadas. La vista anónima /developers/sandbox/data es una muestra pequeña independiente del espacio persistente.

- Usa success después de otro escenario para restaurar la muestra completa.
- Reutiliza IDs al repetir ejemplos y desconecta las conexiones sin uso.

## Producción y consentimiento bancario

Activa Producción explícitamente en la consola, crea una clave de ese entorno y envía POST /connections con {"provider":"malvo"}. RPG se autentica en el servidor con su cuenta Malvo. La respuesta incluye una connect_url temporal para que el titular autorice el acceso. No la registres ni permitas que agentes la abran o previsualicen automáticamente.

La conexión pertenece a la cuenta RPG del desarrollador. Para clientes finales, tu backend debe asociarla al cliente autenticado y verificar esa asociación antes de devolver datos. Nunca entregues una clave RPG al navegador del cliente.

Tras autorizar, tu backend llama a POST /connections/{id}/complete con {"item_id":"ID retornado"}. RPG verifica la propiedad en el proveedor. /developers/connect puede completar el flujo de un desarrollador autenticado; el flujo de clientes finales requiere tu propio backend autenticado.

GET /connections/{id} consulta el estado. POST /sync lee los datos disponibles en el proveedor, sin garantizar una nueva actualización bancaria. POST /reconnect genera un nuevo enlace. DELETE /connections/{id} desconecta y elimina el snapshot local.

- Producción utiliza datos reales y requiere consentimiento del titular.
- La pasarela de cobro no está habilitada; la activación no cobra una tarjeta.
- La cobertura bancaria depende del proveedor y de la institución.

## Un contrato de datos predecible

Las respuestas son JSON. Las listas tienen data, pagination y meta. Los importes son enteros en unidades monetarias menores: 185000 BRL significa R$ 1.850,00. Conserva la moneda al mostrar o sumar. Los importes positivos son entradas; los negativos son salidas.

Conserva los IDs de origen para conciliación. category puede ser texto, un objeto del proveedor o null. Las etiquetas pueden faltar; la falta de clasificación no significa ausencia de actividad. Trata explícitamente las fechas y los campos ausentes.

La respuesta es ilustrativa: IDs, fechas, categorías, registros y saldos pueden variar. Los recursos actuales son cuentas y transacciones; inversiones, informes de crédito, tarjetas y pagos no son recursos adicionales publicados.



```json
{
  "data": [{
    "id": "CONNECTION_ID_tx_1",
    "account_id": "CONNECTION_ID_checking",
    "date": "2026-10-31",
    "description": "Recebimento de cliente",
    "amount_minor": 185000,
    "currency": "BRL",
    "category": "Receitas",
    "tags": ["synthetic", "sandbox"]
  }],
  "pagination": {"offset": 0, "limit": 100, "total": 1, "next_offset": null},
  "meta": {"environment":"sandbox", "provider":"rpg_simulator", "synthetic":true, "status":"connected", "last_synced_at":"2026-10-31T12:00:00Z", "partial":false, "note":null}
}
```

## Paginación, resultados parciales y actualización

Cuentas y transacciones aceptan offset entero no negativo. Cada página contiene hasta 100 registros. Sigue pagination.next_offset hasta null. Las transacciones también admiten account_id. La primera página puede no ser todo el historial.

meta.partial y meta.status describen el snapshot. Muestra last_synced_at y un indicador de datos incompletos. HTTP exitoso puede incluir datos antiguos o parciales. No existe garantía de saldo en tiempo real.

La ingesta del proveedor tiene límites y los historiales grandes pueden ser parciales. Repetir sync no elimina esos límites. Eventos y solicitudes exponen los últimos 100 registros; la consola muestra un historial reciente menor. Guarda el historial que necesite tu producto.



## Ejemplos completos para ejecutar

Usa Node.js 18+ o Python 3 con biblioteca estándar; no hace falta instalar SDK. Exporta RPG_API_KEY en tu terminal con tu clave de sandbox. Guarda el archivo indicado en el primer comentario y ejecútalo.

Define RPG_CONNECTION_ID para reutilizar una muestra. Ambos ejemplos crean una conexión ficticia solo si falta esa variable, leen cuentas y recorren todas las páginas de transacciones. Los fallos incluyen detalles del servidor.



```javascript
// Node.js 18+. Save as quickstart.mjs; run: node quickstart.mjs
const base = 'https://www.rpgcapital.com.br/api/connecta/v1';
const key = process.env.RPG_API_KEY;
if (!key?.startsWith('rpg_test_')) throw new Error('Use a sandbox RPG_API_KEY');
async function api(path, body) {
  const response = await fetch(base + path, {
    method: body ? 'POST' : 'GET',
    signal: AbortSignal.timeout(30000),
    headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
    ...(body ? { body: JSON.stringify(body) } : {})
  });
  const payload = await response.json();
  if (!response.ok) throw new Error(`${response.status} ${payload.error?.code}: ${payload.error?.message} [${payload.request_id}]`);
  return payload;
}
// Reuse RPG_CONNECTION_ID on subsequent runs to avoid creating duplicate fixtures.
const id = process.env.RPG_CONNECTION_ID || (await api('/connections', {
  provider: 'rpg_simulator', scenario: 'success'
})).data.id;
console.log('Connection:', id);
console.log('Accounts:', (await api(`/connections/${id}/accounts`)).data);
const transactions = [];
let offset = 0;
do {
  const page = await api(`/connections/${id}/transactions?offset=${offset}`);
  transactions.push(...page.data);
  offset = page.pagination.next_offset;
} while (offset !== null);
console.log('Transactions:', transactions);
```

```python
# Python 3, standard library only. Save as quickstart.py.
import json, os, urllib.request, urllib.error
BASE = 'https://www.rpgcapital.com.br/api/connecta/v1'
KEY = os.environ.get('RPG_API_KEY', '')
if not KEY.startswith('rpg_test_'):
    raise RuntimeError('Use a sandbox RPG_API_KEY')
def api(path, body=None):
    request = urllib.request.Request(BASE + path,
        data=json.dumps(body).encode() if body is not None else None,
        headers={'Authorization': 'Bearer ' + KEY, 'Content-Type': 'application/json'})
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        details = json.load(error)
        raise RuntimeError(f'{error.code}: {details}') from error
connection_id = os.environ.get('RPG_CONNECTION_ID') or api('/connections',
    {'provider': 'rpg_simulator', 'scenario': 'success'})['data']['id']
print('Connection:', connection_id)
print('Accounts:', api(f'/connections/{connection_id}/accounts')['data'])
transactions, offset = [], 0
while offset is not None:
    page = api(f'/connections/{connection_id}/transactions?offset={offset}')
    transactions.extend(page['data'])
    offset = page['pagination']['next_offset']
print('Transactions:', transactions)
```

## Webhooks firmados y procesamiento seguro

Configura un endpoint HTTPS público por entorno. Copia el secreto cuando se muestre. RPG-Signature contiene t=<timestamp Unix>,v1=<HMAC hexadecimal>. Calcula HMAC-SHA256(secreto, timestamp + "." + cuerpo bruto). Verifica antes de interpretar o procesar; rechaza diferencias de tiempo superiores a cinco minutos.

Utiliza el ID de evento como clave única persistente. Guarda la recepción y encola el trabajo de negocio atómicamente; una reentrega no debe repetir efectos financieros. El ejemplo solo valida firmas y no ejecuta acciones de negocio: añade almacenamiento persistente antes de procesar eventos en producción.

La consola muestra entregas y permite reenvío manual. No hay reintentos automáticos. Los eventos entrantes automáticos de Malvo también requieren configuración de secretos por RPG; si falta, usa complete y sync explícito. El evento indica que debes consultar el estado canónico.



```javascript
// Node.js 18+, no framework. Terminate HTTPS at your public hosting provider.
import http from 'node:http';
import { createHmac, timingSafeEqual } from 'node:crypto';
const secret = process.env.RPG_WEBHOOK_SECRET;
if (!secret) throw new Error('Set RPG_WEBHOOK_SECRET');
http.createServer(async (req, res) => {
  if (req.method !== 'POST' || req.url !== '/webhooks/rpg') { res.writeHead(404).end(); return; }
  const chunks = []; let size = 0;
  for await (const chunk of req) {
    size += chunk.length;
    if (size > 1_000_000) { res.writeHead(413).end(); return; }
    chunks.push(chunk);
  }
  const raw = Buffer.concat(chunks);
  const parts = String(req.headers['rpg-signature'] || '').split(',');
  const t = parts.find(p => p.startsWith('t='))?.slice(2) || '';
  if (!/^\d+$/.test(t) || Math.abs(Date.now()/1000 - Number(t)) > 300) { res.writeHead(401).end(); return; }
  const expected = createHmac('sha256', secret).update(t + '.').update(raw).digest();
  const valid = parts.filter(p => p.startsWith('v1=')).some(p => {
    const hex = p.slice(3); if (!/^[a-f0-9]{64}$/.test(hex)) return false;
    return timingSafeEqual(Buffer.from(hex, 'hex'), expected);
  });
  if (!valid) { res.writeHead(401).end(); return; }
  try {
    const event = JSON.parse(raw.toString('utf8'));
    // REQUIRED: persist event.id with a UNIQUE constraint in your database,
    // and enqueue the business effect in the same transaction (outbox pattern).
    // Duplicate event IDs must become a no-op, including after restarts.
    // This signature example intentionally performs no business effect.
    res.writeHead(200, {'Content-Type':'application/json'}).end(JSON.stringify({received:true,id:event.id}));
  } catch { res.writeHead(400).end(); }
}).listen(Number(process.env.PORT || 3000));
```

## Errores con una siguiente acción clara

Los errores incluyen error.code, error.message y request_id; también se devuelve X-Request-ID. Comparte el ID de solicitud al pedir soporte, nunca claves ni contenido financiero.

Reintenta fallos transitorios con espera exponencial limitada y variación aleatoria. Respeta Retry-After. No repitas ciegamente POST /connections tras un timeout ambiguo: primero lista conexiones para evitar duplicados. No hay contrato publicado de clave de idempotencia.



```json
{
  "error": {
    "code": "environment_mismatch",
    "message": "Esta chave pertence a outro ambiente."
  },
  "request_id": "uuid"
}
```

## Crea un dashboard útil

Crea una ruta en el servidor que lea la conexión asociada al cliente autenticado. Recorre cuentas y transacciones, conserva IDs y muestra saldos por moneda. Separa entradas y salidas; agrupa por mes y categoría conservando las desconocidas.

Muestra estado, última sincronización, datos parciales y reconexión junto a los datos. Un 401 debe ofrecer autenticación o reparación de credenciales. En una caída conserva el último snapshot con fecha. Prueba los cinco estados antes de producción.



## Instrucciones para agentes de programación

Lee esta guía y OpenAPI antes de generar código. Usa el Markdown inglés como entrada compacta. Conserva placeholders para credenciales; indica al usuario que cree una clave en la consola y la guarde en los secretos del backend.

Genera integración de servidor, asociación de propietario, paginación, firmas y errores con request IDs. Empieza por sandbox. No inventes endpoints MCP, skills instalables, paquetes SDK, cobertura bancaria, precios ni pagos.

El agente puede crear código y explicar la autorización. Login Google, consentimiento bancario y activación deben ser visibles al usuario y respetar las herramientas de autenticación y permisos del entorno del agente.



## Preparación para producción y límites actuales

Antes del lanzamiento: rota credenciales de desarrollo, separa secretos por entorno, verifica propiedad de clientes, prueba consentimiento expirado y datos parciales, valida firmas del cuerpo bruto y deduplicación persistente, y confirma monitorización y soporte.

Disponible: login Google, claves RPG, simulador persistente, adaptador Malvo de producción, cuentas, transacciones, eventos, diagnóstico de solicitudes y webhooks firmados con reenvío manual. El sandbox Malvo y los eventos automáticos dependen de configuración del servidor.

No hay servidor MCP publicado, SDK empaquetado, reintentos automáticos ni pasarela de cobro. La API anterior de comercios /api/public/v1 y /developers/docs usa otro contrato y no admite claves Connecta. Contacto: comercial@rpgcapital.com.br.



## Endpoint

- GET /connections: Listar tus conexiones
- POST /connections: Crear conexión ficticia o de consentimiento
- GET /connections/{id}: Consultar estado de conexión
- GET /connections/{id}/accounts: Consultar cuentas y saldos; paginación offset
- GET /connections/{id}/transactions: Consultar transacciones; offset y account_id
- POST /connections/{id}/simulate: Cambiar escenario del simulador RPG
- POST /connections/{id}/sync: Leer el último snapshot del proveedor
- POST /connections/{id}/complete: Verificar y vincular el item autorizado
- POST /connections/{id}/reconnect: Generar nuevo enlace de consentimiento
- DELETE /connections/{id}: Desconectar y borrar snapshot local
- GET /events: Consultar últimos 100 eventos
- GET /requests: Consultar últimas 100 solicitudes

## Diccionario de datos

- id: ID estable del origen para conciliación
- account_id: Cuenta asociada a la transacción
- name / type: Nombre y tipo de cuenta
- balance_minor: Saldo entero en unidad monetaria menor
- amount_minor: Importe entero con signo en unidad menor
- currency: Código de moneda; no mezcles monedas
- date: Fecha de origen; trata valores ausentes
- description: Descripción original
- category: Texto, objeto o null
- tags: Clasificación; cobertura variable
- pagination.next_offset: Siguiente offset o null al terminar
- meta.synthetic / provider: Dato ficticio y proveedor de origen
- meta.status / partial: Estado y si el snapshot está incompleto
- meta.last_synced_at: Fecha del snapshot; muéstrala en la interfaz

## Estado

- 401 invalid_api_key: Clave ausente, inválida o revocada. Sustituye la credencial del servidor; una cookie no es una clave API.
- 403 environment_mismatch: Encabezado y clave no coinciden. Corrige el entorno o elimina el encabezado opcional.
- 403 production_inactive: Producción no activada. Actívala explícitamente en la consola.
- 404 not_found: Conexión no encontrada para esta cuenta y entorno. Comprueba ID, propietario y entorno.
- 409 authorization_pending: Autorización incompleta. Completa el consentimiento antes de sincronizar.
- 409 connection_limit / key_limit: Límite de recursos activos. Revoca una clave o desconecta una conexión sin uso.
- 429 rate_limited: Límite de solicitudes por clave. Espera Retry-After y reduce consultas.
- 502 provider_error / provider_auth_error: Fallo o autenticación rechazada del proveedor. Reintenta fallos transitorios; si persiste contacta con RPG.
- 503 provider_not_configured: Credenciales del proveedor no disponibles. Usa el simulador RPG o contacta con soporte.
- 400 invalid_scenario / invalid_offset: Parámetro inválido. Usa escenario admitido y offset entero no negativo.
