API pública · v1
Expone verbos, no filas.
No lista clientes, no lee facturas, no publica tablas. Lo único que sabe hacer es ejercer una acción declarada como pública, con la autorización que su dueño encendió, y contarte con precisión qué pasó.
Idempotency-Key no se ejecuta dos veces.En 30 segundos
Emite una clave en tu workspace, enciende el permiso de la acción, y llama.
curl -X POST https://niiko.org/api/v1/actions/miira.lead_create \
-H "Authorization: Bearer nk_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ … }'npm install @niiko/sdkpip install niikoAutenticación
Una clave del workspace, no una sesión de persona: autentica una integración, no a
alguien. Authorization: Bearer nk_…. Nace acotada a unos alcances y con caducidad, y
se ve una sola vez, al crearla.
Idempotencia
Manda Idempotency-Key con un valor único por encargo. Un reintento por timeout
con la misma clave no vuelve a ejecutar: contesta duplicate con lo que pasó
la primera vez. Sin esa cabecera, un reintento es un encargo nuevo. La protección dura 24 h.
Tres desenlaces, no dos
Un contrato de dos desenlaces obliga a que el tercero se disfrace de otro, y el que se disfraza siempre es el que más se tarda en entender.
| status | código | qué significa |
|---|---|---|
| done | 200 | Hecho. Trae el output. |
| pending_approval | 202 | Queda esperando una firma humana y puede terminar horas después. Te avisamos por webhook cuando se resuelva. |
| refused | 4xx | No se hizo, y reason dice por qué. |
Webhooks
Registra un destino y te avisamos cuando algo se resuelve — sobre todo lo que quedó
pending_approval, que por definición termina cuando ya colgaste.
La firma
Cada entrega trae Niiko-Signature con el instante dentro de lo
firmado, no solo el cuerpo. Firmar solo el cuerpo deja una firma válida para siempre: quien capture una
entrega puede repetirla mañana. Verifica que el instante sea reciente y que la firma case.
Reintentos
Si tu servidor no contesta 2xx, se reintenta con espera creciente. Un 4xx que no sea 408 ni 429 no se reintenta: significa que el destino está mal, no que esté ocupado. Tras muchos fallos seguidos, ese destino se apaga solo — y te lo decimos.
Versiones
Una acción lleva su versión en el contrato. Cuando una se retira sale con la cabecera
Sunset y las fechas dentro, y el motivo es version_retired — no un 404. La
diferencia importa: un 404 dice «no existe» y manda a buscar un error de escritura; un 410 dice
«existió, ya no» y manda a leer el aviso.
Las acciones abiertas
miira.lead_create
v1
POST /api/v1/actions/miira.lead_create
Hacen falta los dos: una clave con ese alcance, y que el dueño del workspace haya encendido ese permiso. Tener la clave no basta — son dos decisiones distintas y las toma gente distinta.
Lo que mandas
| campo | tipo | reglas | |
|---|---|---|---|
submissionId | string (uuid) | obligatorio | |
source | "web_form" | "referral" | "api" | "import" | "event" | "other" | obligatorio | |
name | string | opcional | máx. 256 |
email | string (email) | opcional | máx. 320 |
phone | string | opcional | máx. 40 |
fields | map<string, unknown> | opcional | |
consent | object | opcional |
Lo que recibes
| campo | tipo | reglas | |
|---|---|---|---|
outcome | "created" | "existing" | "ambiguous" | "rejected" | obligatorio | |
clientId | string | null | obligatorio | |
reason | "honeypot" | "invalid_email" | "disposable_email" | "invalid_identity" | null | obligatorio |
Ejemplo
curl -X POST https://niiko.org/api/v1/actions/miira.lead_create \
-H "Authorization: Bearer nk_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"submissionId": "01890a5d-ac96-774b-bcce-b302099a8057",
"source": "web_form"
}'import { Niiko } from "@niiko/sdk";
const niiko = new Niiko({ apiKey: process.env.NIIKO_API_KEY! });
const r = await niiko.createLead({
submissionId: "01890a5d-ac96-774b-bcce-b302099a8057",
source: "web_form",
}, crypto.randomUUID());
if (r.status === "done") console.log(r.output);
else if (r.status === "refused") console.error(r.reason);import os, uuid
from niiko import Niiko
niiko = Niiko(api_key=os.environ["NIIKO_API_KEY"])
r = niiko.create_lead(
submissionId="01890a5d-ac96-774b-bcce-b302099a8057",
source="web_form",
idempotency_key=str(uuid.uuid4()),
)
print(r.output if r.status == "done" else r.reason)Motivos por los que puede no hacerse
Del vocabulario que esta acción declara. Un motivo fuera de esta lista es un fallo del servidor, no un estado del producto.
honeypotinvalid_emaildisposable_emailinvalid_identity
Los motivos, y qué hacer con cada uno
El código HTTP lo elige el remedio, no el parecido: quién tiene que hacer algo, y qué.
| motivo | código | qué significa |
|---|---|---|
invalid_input | 400 | El payload no valida contra el contrato. Es lo único de esta lista que puedes arreglar tú solo. |
unauthorized | 401 | La clave no vale. No se distingue entre inexistente, revocada, vencida o sin el alcance: distinguirlo convertiría esta puerta en un oráculo para quien esté probando claves. El estado exacto de tu clave lo ves en tu panel. |
grant_off | 403 | El workspace no ha encendido esta capacidad. No es una prohibición: es una perilla que le toca a su dueño. |
origin_denied | 403 | Este origen no puede ejercer esta acción, y no hay perilla que lo cambie. |
not_autonomous | 409 | Necesita una aprobación y este camino no puede esperarla. |
stale | 409 | El mundo cambió entre que lo pediste y que se iba a hacer. Vuelve a leer antes de reintentar. |
duplicate | 409 | Ya pasó. Trae la clave y cómo terminó la primera vez. No se devuelve el resultado original: solo se guarda QUE ocurrió. |
in_flight | 409 | Otra petición con la misma clave está corriendo ahora. Trae Retry-After. |
not_exposed | 404 | Esa acción no está abierta al exterior. Puede existir dentro del producto; lo que no existe es su puerta. |
version_retired | 410 | Esa versión ya se retiró. Sale con Sunset y las fechas dentro: existió, ya no. |
rate_limited | 429 | Cupo por clave. Trae Retry-After. |
internal_error | 500 | Un fallo nuestro. Es lo único de esta lista que no es una decisión ni un estado. |
Clientes
Los dos salen del mismo manifiesto que esta página, en la misma corrida. No validan tu input —podrían, y sería peor: un cliente que valida puede discrepar del servidor, y la dirección probable del error es la peligrosa—. Lo que aportan es la forma: qué campos hay, cuáles son obligatorios y qué te puede contestar, en tu editor, antes de mandar nada.
npm install @niiko/sdkpip install niiko