Cambia client_id y client_secret por un token Bearer de una hora, solo de servidor a servidor.
← Volver a la documentación de la API
Tu primera factura en 6 pasos
- Genera la credencial en el panel, en Facturación → Configuración → Acceso API, y copia el client_secret de inmediato: solo se muestra una vez.
- Envía POST /v1/auth/token con grant_type client_credentials, client_id y client_secret.
- Recibes access_token, token_type Bearer, expires_in 3600 y la lista de scopes concedidos.
- Incluye el token en una cabecera Authorization: Bearer <access_token> en todas las demás llamadas.
- Comprueba qué has obtenido realmente con GET /v1/me: tenant, scopes y los cupos de peticiones actuales.
Todos los endpoints
| Método | Endpoint | Scope | Idempotency-Key | Peticiones / minuto |
|---|---|---|---|---|
| POST | /auth/token Cambiar las credenciales por un token de acceso | — | — | 120 |
| GET | /me Tenant, scopes y límites actuales | invoices:read | — | 120 |
Qué es la API de 4invoices
bash
curl -sS -X POST https://api.4invoices.net/v1/auth/token \
-H 'Content-Type: application/json' \
-d '{
"grant_type": "client_credentials",
"client_id": "cli_XXXXXXXXXXXX",
"client_secret": "sec_XXXXXXXXXXXXXXXXXXXX"
}'
# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600,
# "scope":"invoices:read invoices:write efactura:submit"}bash
curl -sS https://api.4invoices.net/v1/me \
-H "Authorization: Bearer $ACCESS_TOKEN"
# → tenant, scopes concedidos y cupos de peticiones actualesjavascript
// Guarda el token en caché; no generes uno por petición.
let cached = { token: "", expiresAt: 0 };
async function getToken() {
if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch("https://api.4invoices.net/v1/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "client_credentials",
client_id: process.env.FOURINVOICES_CLIENT_ID,
client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(`token ${res.status}`);
const data = await res.json();
cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}https://api.4invoices.net/v1 · https://api.4invoices.net/v1/openapi.json
Scopes
- invoices:read
- invoices:write
- clients:read
- clients:write
- efactura:read
- efactura:submit
Solo servidor a servidor. Nunca llames a la API desde JavaScript de navegador: el client_secret llegaría al visitante y CORS bloquea cualquier origen de todos modos.
Errores
| Estado | Tipo | Código |
|---|---|---|
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 403 | permission_error | insufficient_scope |
Errores frecuentes
- El endpoint de tokens tiene su propio límite por IP. Un token por proceso, renovado antes de que caduque, no uno por petición.
- El client_secret se almacena solo como hash; si lo pierdes, generas una credencial nueva y revocas la anterior.
- Emite credenciales distintas para el ERP, la tienda y los scripts internos, cada una con scopes mínimos. Revocar una no afecta a las demás.
- CORS no admite ningún origen de navegador: la API es servidor a servidor por diseño.
Preguntas y respuestas para desarrolladores
¿Qué ocurre si el client_secret es incorrecto?
Recibes un 401 con el código invalid_client. Un secreto demasiado corto falla antes, en la validación, con 422 validation_failed y param client_secret.
¿Existe un entorno de pruebas aparte?
No. Hay un único host de producción. Prueba con proformas (document_kind proforma) y con /invoices/preview-totals, que no consume ningún número fiscal.
¿Puedo reducir los scopes de una credencial existente?
Sí, desde el panel. Los tokens ya emitidos conservan los scopes con los que se generaron hasta que caducan, en un plazo máximo de una hora.