REST API v1

Gestione agentes, lea conversaciones y contactos y chatee desde su propio backend, con HTTP simple.

curl https://api.onetype.ai/v1/agents -H "Authorization: Bearer sk_live_…"

Autenticación y claves

Cree claves en Developer › API keys (propietario o administrador). Una clave se muestra una sola vez.

ClavePrefijoUsoPuede
Secretsk_live_Solo sus servidoresTodo lo que puede hacer un administrador del workspace, salvo las acciones de identidad y de propiedad
Restrictedrk_live_Servidores, CI, herramientas de automatizaciónSolo los permisos que marque al crearla (nunca más que los suyos)

Las claves de API vienen con Premium. Envíe la clave como Authorization: Bearer <key>. Las claves pertenecen a un workspace, y una clave nunca puede leer los datos de otro workspace. Mantenga las claves secret y restricted fuera de los navegadores, las aplicaciones móviles y los repositorios. Si una se filtra, revóquela en el dashboard y la siguiente solicitud con ella falla.

Recursos

EndpointScope (permiso)Estado
GET/POST /v1/agents, GET/PATCH/DELETE /v1/agents/:idagents.read, agents.update, …Disponible
GET /v1/conversations?agent&status&created[gte], GET /v1/conversations/:id, GET /v1/conversations/:id/messagesconversations.readDisponible
POST /v1/conversations { agent }conversations.write (solo claves)Disponible
POST /v1/conversations/:id/messages { content, stream? }conversations.writeDisponible
GET /v1/contacts?agent, GET /v1/contacts/:idcontacts.readDisponible
POST/DELETE /v1/mcp, /v1/mcp/agents/:agentvéase MCPDisponible

El conocimiento y el uso se gestionan en el dashboard.

Un contacto pertenece a un agente (agent, un id agt_… ): la misma persona en los sitios de dos de sus agentes son dos contactos. ?agent=agt_… lista los de un agente.

Los objetos públicos usan campos snake_case e ids con prefijo (las rutas de visitante de un chat personalizado usan camelCase, por ejemplo isAiGenerated; snake_case, is_ai_generated, existe solo en los formatos /v1 ). Nunca contienen campos internos. Cada objeto de mensaje tiene is_ai_generated (true para las respuestas del agente, false para las de su equipo), como exige el Reglamento de IA de la UE.

OpenAPI: https://api.onetype.ai/v1/openapi.json, generado a partir de los mismos esquemas con los que valida la API.

Estados de las conversaciones

Los objetos de conversación (en /v1 y en los webhooks conversation.* ) llevan status y, cuando están resueltos, resolved_by:

statusSignificado
openEn curso
escalatedPasada a su equipo; solo una persona la cierra
resolvedCerrada con una respuesta. resolved_by: ai (cerrada automáticamente tras una buena respuesta), member (una persona de su equipo), visitor (el visitante lo confirmó) o idle (conversaciones cerradas antes del 2026-09-24)
unresolvedCerrada automáticamente después de que la última respuesta del agente dijera que no lo sabía, o recibiera una valoración negativa. Vuelve a open si el visitante vuelve a escribir
failedNo se pudo responder por un error, o la cerró su equipo como no resuelta (también tras un traspaso)

Una conversación que el agente gestiona solo se cierra automáticamente 30 minutos después de su último mensaje. Las conversaciones con traspaso, retenidas por su equipo o a la espera de un mensaje de visitante sin respuesta no se cierran nunca automáticamente. Pueden añadirse nuevos valores; trate los desconocidos como open.

Paginación

Los endpoints de lista usan cursores:

curl "https://api.onetype.ai/v1/conversations?limit=50" -H "Authorization: Bearer sk_live_…"
{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "b3BhcXVl…" }

Pase cursor=<next_cursor> para la página siguiente. limit es 20 de forma predeterminada, con un máximo de 100. Los cursores son opacos y se mantienen estables cuando llegan elementos nuevos mientras pagina.

Idempotencia

Envíe Idempotency-Key: <any unique string> en cualquier POST para que los reintentos sean seguros. El primer resultado se conserva durante 24 horas, y un reintento con la misma clave lo devuelve en lugar de repetir el trabajo.

curl -X POST https://api.onetype.ai/v1/conversations \
  -H "Authorization: Bearer sk_live_…" \
  -H "Idempotency-Key: 6f1c9f2e-3b1d-4a0e-9d0f-2f6f0b3c9a11" \
  -H "Content-Type: application/json" \
  -d '{"agent":"YOUR_AGENT_ID"}'
  • Una respuesta reproducida lleva Idempotent-Replayed: true.
  • Las claves tienen de 1 a 255 caracteres ASCII imprimibles. Cualquier otra cosa es 400 BAD_IDEMPOTENCY_KEY.
  • La misma clave con un cuerpo distinto es 409 IDEMPOTENCY_KEY_REUSED. Use una clave nueva para una solicitud nueva.
  • Mientras la primera solicitud con una clave sigue en curso, un reintento recibe 409 IDEMPOTENCY_KEY_IN_USE. Espere y vuelva a intentarlo.
  • Los errores de validación no se almacenan (corrija la solicitud y reintente con la misma clave). Un rechazo producido por el propio trabajo (p. ej., un límite del plan) se almacena y reproduce.
  • Las claves son privadas de su workspace y de su clave.

Errores

Todo error tiene la misma forma, además de una cabecera x-request-id . Incluya el id de la solicitud cuando contacte con soporte.

{ "error": { "status": 422, "code": "VALIDATION_FAILED", "message": "content: required", "fields": { "content": "Required" } } }
EstadoCuándoCódigos típicos
400La solicitud no es válidaVALIDATION_FAILED (véase fields), BAD_IDEMPOTENCY_KEY
401Falta la clave, es incorrecta o está revocadaUNAUTHENTICATED
403La clave no tiene el permisoPERMISSION_DENIED
404No encontrado en su workspace (los objetos de otros workspaces aparecen igual que si no existieran)NOT_FOUND
409ConflictosIDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_USE, INVALID_TRANSITION
402 / 422Límites del plan y reglas de negocioQUOTA_EXCEEDED, REFUSED (el mensaje explica el motivo)
429Demasiadas solicitudesRATE_LIMITED
5xxFallo nuestrosiempre INTERNAL, con el id de la solicitud. Si sigue ocurriendo, escriba a support@onetype.ai con el id de la solicitud

code es un valor estable. Base su gestión en él, no en message.

Límites de frecuencia

Por clave: 100 solicitudes cada 10 segundos y 20 mensajes de chat cada 10 segundos. Los planes superiores traen límites más altos. Cada respuesta lleva:

RateLimit-Limit: 100
RateLimit-Remaining: 87
RateLimit-Reset: 6

Un 429 también lleva Retry-After (segundos). Espere ese tiempo y reintente. No reintente en un bucle sin pausa.

La autenticación fallida se limita por dirección de red. Las solicitudes con una clave incorrecta o revocada cuentan contra la dirección de la que proceden. Si muchos clientes comparten una dirección pública (un NAT corporativo, un proveedor de CI), un cliente mal configurado puede agotar el presupuesto, y entonces incluso las solicitudes con una clave válida de esa dirección reciben 429 RATE_LIMITED hasta que se restablece la ventana. Trátelo como cualquier 429: respete Retry-After y después corrija el cliente que envía la clave incorrecta.

async function call(url: string, init: RequestInit, attempts = 3): Promise<Response>
{
    const response = await fetch(url, init);
    if (response.status !== 429 || attempts <= 1)
    {
        return response;
    }
    const waitSeconds = Number(response.headers.get("retry-after") ?? "1");
    await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
    return call(url, init, attempts - 1);
}

Chat desde su backend

Cree una conversación para un agente y luego envíele mensajes. Cada respuesta se redacta, almacena y factura exactamente igual que una respuesta del widget.

curl -X POST https://api.onetype.ai/v1/conversations \
  -H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
  -d '{"agent":"YOUR_AGENT_ID"}'
# → { "id": "cnv_…", "object": "conversation", "status": "open", … }

curl -X POST https://api.onetype.ai/v1/conversations/cnv_…/messages \
  -H "Authorization: Bearer sk_live_…" -H "Idempotency-Key: order-1234-question-1" -H "Content-Type: application/json" \
  -d '{"content":"Where is order 1234?"}'
# → { "id": "msg_…", "object": "message", "role": "assistant", "is_ai_generated": true, "parts": [ … ], … }

Los parts de la respuesta contienen partes text y una parte source-document por cada fuente citada (title y url para páginas web).

Streaming

Envíe "stream": true para recibir la respuesta a medida que se redacta, como server-sent events en el formato AI SDK UI Message Stream v1 . La respuesta lleva x-vercel-ai-ui-message-stream: v1, y el data de cada evento es una parte JSON: start, text-start, text-delta (con delta), text-end, source-document, finish (con messageMetadata.outcome), error o abort. El stream termina con data: [DONE].

const response = await fetch(`https://api.onetype.ai/v1/conversations/${conversationId}/messages`, {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.ONETYPE_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
    body: JSON.stringify({ content: "How do refunds work?", stream: true }),
});

const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
for (;;)
{
    const { value, done } = await reader.read();
    if (done) break;
    buffer += value;
    const events = buffer.split("\n\n");
    buffer = events.pop() ?? "";
    for (const event of events)
    {
        const data = event.split("\n").find((line) => line.startsWith("data: "))?.slice(6);
        if (data === undefined || data === "[DONE]") continue;
        const part = JSON.parse(data);
        if (part.type === "text-delta") process.stdout.write(part.delta);
        if (part.type === "source-document") console.log(`\n[source] ${part.title}`);
    }
}

Con el Vercel AI SDK en su propio servidor, puede pasar la respuesta directamente a useChat, porque es el mismo formato.

  • Desconectarse no cancela la respuesta. La respuesta se sigue redactando, almacenando y facturando.
  • Reintentar un stream terminado es seguro. Reintente con el mismo Idempotency-Key y recibirá la respuesta almacenada como JSON (estado 200), no una segunda respuesta.

Versionado

/v1 solo cambia de forma aditiva: campos nuevos, endpoints nuevos, tipos de evento nuevos. Su código debe ignorar los campos que no conozca. Si alguna vez hace falta un cambio incompatible, se publicará como una versión nueva que se elige por clave, con aviso previo.