Empezar
Autenticación
Las tres formas de llamar a la API: claves de empresa con alcances, token de socio con client_credentials y la conexión con un botón (OAuth 2.0 con PKCE).
Actualizado el
Hay tres formas de autenticarte. Elige según de quién es la cuenta que vas a usar:
| Si eres… | Usa | Token |
|---|---|---|
| La empresa (o su programador) y emites con tu propia cuenta | Clave de empresa | sfp_live_… / sfp_test_… |
| Un socio que da de alta a sus clientes como empresas | Clave de socio (OAuth 2 client_credentials) | sfp_pat_… (1 hora) |
| Un sistema cuyos clientes ya tienen cuenta en Sistema Facturación Perú | Conectar con Sistema Facturación Perú (OAuth 2 + PKCE) | sfp_oat_… (1 hora) |
En los tres casos el token va solo en la cabecera Authorization: Bearer <token>. En la URL o en el cuerpo responde
401 (clave_en_url en la API v1, token_en_url en la de socios), porque quedaría guardado en los registros de los
servidores.
Claves de empresa
Se crean en Configuración › Sistema › Integraciones › API (desde el Plan PRO). Cada empresa puede tener varias, cada una con:
- un nombre (por ejemplo, «Tienda en línea»), para reconocerla en el registro de llamadas;
- un usuario: la clave actúa como él, con sus funciones, su local y sus series;
- alcances: lo que la clave puede hacer, aunque el usuario pueda más;
- un local fijo, si quieres que solo emita en uno;
- una fecha de vencimiento, si quieres.
Formato: sfp_live_ + 40 caracteres en producción y sfp_test_ + 40 en pruebas. Guardamos solo su huella: la ves
una sola vez al crearla. Si la pierdes, revócala y crea otra. La pantalla muestra cuándo se usó por última vez.
Alcances
| Alcance | Permite |
|---|---|
comprobantes.emitir |
Emitir comprobantes, notas de venta, cotizaciones, retenciones, percepciones y liquidaciones; reenviar a SUNAT |
comprobantes.leer |
Ver comprobantes, descargar PDF, XML y CDR, enviarlos por correo o WhatsApp |
comprobantes.anular |
Anular (baja o resumen) |
guias.emitir / guias.leer |
Emitir y ver guías de remisión |
cobros.registrar |
Registrar cobros |
catalogo.leer / catalogo.escribir |
Ver y cambiar clientes, productos y locales |
stock.leer |
Ver stock por almacén, lote y serie |
consultas |
RUC, DNI y tipo de cambio |
webhooks.gestionar |
Crear y borrar webhooks |
Cada llamada revisa dos cosas: que la clave tenga el alcance y que su usuario tenga la función en el sistema.
- Falta el alcance → 403
scope_faltante, con la cabeceraX-SFP-Scope-Requiredque dice cuál. - Falta la función del usuario → 403
sin_permiso. - El plan no incluye la API → 403
plan_sin_api. - Pago vencido o cuenta suspendida → 423
cuenta_bloqueada.
Da a cada clave solo lo que necesita. Una tienda en línea que emite boletas no necesita
comprobantes.anularnicatalogo.escribir. Si la clave se filtra, el daño queda acotado.
El token antiguo
El token del usuario que usa la app móvil también vale en la v1, con todos los alcances; en el registro de llamadas sale como «legacy». Funciona, pero te recomendamos pasar a una clave v1: tiene alcances, vencimiento y se revoca sin tocar al usuario.
curl https://miempresa.sistemafacturacionperu.com/api/v1/yo \
-H "Authorization: Bearer $SFP_API_KEY"
<?php
$headers = ['Authorization: Bearer ' . getenv('SFP_API_KEY'), 'Accept: application/json'];
const headers = { Authorization: `Bearer ${process.env.SFP_API_KEY}`, Accept: 'application/json' };
headers = {"Authorization": f"Bearer {os.environ['SFP_API_KEY']}", "Accept": "application/json"}
Claves de socio
Si tienes muchos clientes propios y quieres darlos de alta tú mismo, eres socio. Te entregamos una credencial
(client_id sfp_partner_… y client_secret sfp_secret_…) y con ella pides un token de 1 hora. Es OAuth 2
client_credentials: cualquier librería de OAuth sirve.
curl -s https://api.sistemafacturacionperu.com/socios/v1/token \
-u "$SFP_CLIENT_ID:$SFP_CLIENT_SECRET" \
-d grant_type=client_credentials
<?php
$ch = curl_init('https://api.sistemafacturacionperu.com/socios/v1/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => getenv('SFP_CLIENT_ID') . ':' . getenv('SFP_CLIENT_SECRET'),
CURLOPT_POSTFIELDS => http_build_query(['grant_type' => 'client_credentials']),
]);
$token = json_decode(curl_exec($ch), true)['access_token']; // sfp_pat_…, vale 1 hora
const basic = Buffer.from(`${process.env.SFP_CLIENT_ID}:${process.env.SFP_CLIENT_SECRET}`).toString('base64');
const res = await fetch('https://api.sistemafacturacionperu.com/socios/v1/token', {
method: 'POST',
headers: { Authorization: `Basic ${basic}`, 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'client_credentials' }),
});
const { access_token } = await res.json(); // sfp_pat_…, vale 1 hora
r = requests.post(
"https://api.sistemafacturacionperu.com/socios/v1/token",
auth=(os.environ["SFP_CLIENT_ID"], os.environ["SFP_CLIENT_SECRET"]),
data={"grant_type": "client_credentials"},
timeout=30,
)
token = r.json()["access_token"] # sfp_pat_…, vale 1 hora
Con ese token das de alta empresas y creas, para cada una, una clave de empresa sfp_live_ con la que emites en su
API v1. Todo el detalle, los precios y las bolsas están en la API de socios.
Conectar con Sistema Facturación Perú
Si tu sistema tiene clientes que ya usan Sistema Facturación Perú, no les pidas que copien una clave: ponles un botón «Conectar con Sistema Facturación Perú». Es OAuth 2.0 con authorization code y PKCE S256 obligatorio, como Google, Mercado Pago o Stripe.
- Tu botón abre una ventana emergente en
https://api.sistemafacturacionperu.com/conectarcon tuclient_id, turedirect_uri, unstatey elcode_challengede PKCE. - El usuario escribe su RUC y entra con su usuario y contraseña de siempre, en el subdominio de su empresa. Su contraseña nunca pasa por tu sistema.
- Pulsa «Permitir» en la pantalla que le muestra tu aplicación y los permisos que pides.
- Vuelve a tu
redirect_uricon uncodede un solo uso (5 minutos) y tustate. - Tu servidor canjea el código en
POST /oauth/tokencon tu secreto y elcode_verifier, y recibe un token de 1 hora, uno de renovación y la dirección de la API de esa empresa. - Emites en
empresa.api_urlconAuthorization: Bearer sfp_oat_….
Así se genera el par PKCE:
import { randomBytes, createHash } from 'node:crypto';
const b64url = (buf) => buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const codeVerifier = b64url(randomBytes(48)); // 64 caracteres: guárdalo en la sesión
const codeChallenge = b64url(createHash('sha256').update(codeVerifier).digest());
const state = b64url(randomBytes(24));
<?php
$b64url = fn (string $bin) => rtrim(strtr(base64_encode($bin), '+/', '-_'), '=');
$codeVerifier = $b64url(random_bytes(48));
$codeChallenge = $b64url(hash('sha256', $codeVerifier, true));
$state = $b64url(random_bytes(24));
$_SESSION['sfp'] = ['verifier' => $codeVerifier, 'state' => $state];
import base64, hashlib, secrets
b64url = lambda b: base64.urlsafe_b64encode(b).rstrip(b"=").decode()
code_verifier = b64url(secrets.token_bytes(48))
code_challenge = b64url(hashlib.sha256(code_verifier.encode()).digest())
state = b64url(secrets.token_bytes(24))
En el Plan Básico la conexión sirve solo para emitir; en PRO y Empresa, para todo. El botón completo, el canje, la renovación y la desconexión están en Conectar con Sistema Facturación Perú.
Buenas prácticas
- Guarda claves, secretos y tokens en variables de entorno o en un gestor de secretos. Nunca en el repositorio, en una app móvil ni en el navegador.
- Una clave por sistema o integración: si un sistema se da de baja, revocas su clave y los demás siguen.
- Pon fecha de vencimiento a las claves de integraciones temporales.
- Revisa el Registro de llamadas (Configuración › Sistema › Integraciones › API › Registro): guarda 30 días de llamadas con fecha, clave, ruta, resultado, tiempo e IP.