Referencia OpenAPI
Consulta y descarga la especificación OpenAPI 3.1.0 del contrato público de Nahui Connect v1.
La especificación OpenAPI pública declara dos endpoints de salida y el webhook message.received. La copia descargable se conserva sin modificaciones respecto al paquete fuente.
Endpoints públicos de salida
POST/v1/connect/relay/{connectionId}/reply
POST/v1/connect/relay/{connectionId}/template
Especificación
openapi: 3.1.0
info:
title: Nahui Connect API
version: 1.0.0
description: >-
API pública para responder conversaciones de WhatsApp recibidas mediante
Nahui Connect. Los destinatarios se identifican exclusivamente por
conversationId; no se aceptan teléfonos ni destinatarios arbitrarios.
contact:
name: Nahui Studio
url: https://nahui.studio
license:
name: Software y API propietarios de Nahui Studio
identifier: LicenseRef-Nahui-Proprietary
servers:
- url: https://api.nahui.studio
description: Producción
tags:
- name: Mensajes
description: Respuestas de texto a conversaciones existentes.
- name: Plantillas
description: Plantillas de WhatsApp aprobadas para conversaciones existentes.
security:
- connectionApiKey: []
paths:
/v1/connect/relay/{connectionId}/reply:
post:
operationId: replyToConnectConversation
tags: [Mensajes]
summary: Responder con texto a una conversación existente
description: >-
Envía texto a un conversationId emitido previamente por el webhook de
la misma conexión. No acepta campos de destinatario.
parameters:
- $ref: '#/components/parameters/ConnectionId'
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReplyRequest'
examples:
simple:
value:
conversationId: ncv_8354e263-3958-4602-b54c-6b6fd84720c2
message:
text: Tu cita quedó confirmada.
responses:
'200':
description: Mensaje enviado o solicitud duplicada ya aceptada.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/RelaySuccess'
- $ref: '#/components/schemas/DuplicateSuccess'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/BalanceExhausted'
'404':
$ref: '#/components/responses/ConversationNotFound'
'413':
$ref: '#/components/responses/PayloadTooLarge'
'429':
$ref: '#/components/responses/RateLimited'
'502':
$ref: '#/components/responses/UpstreamFailure'
/v1/connect/relay/{connectionId}/template:
post:
operationId: sendConnectTemplate
tags: [Plantillas]
summary: Enviar una plantilla de WhatsApp aprobada
description: >-
Envía una plantilla existente en Meta a una conversación que nació de
un mensaje entrante. Límite de 5 plantillas por minuto por conexión.
parameters:
- $ref: '#/components/parameters/ConnectionId'
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateRequest'
examples:
simple:
value:
conversationId: ncv_8354e263-3958-4602-b54c-6b6fd84720c2
templateName: recordatorio_cita
language: es_MX
withBodyParameters:
value:
conversationId: ncv_8354e263-3958-4602-b54c-6b6fd84720c2
templateName: recordatorio_cita
language: es_MX
components:
- type: body
parameters:
- type: text
text: Daniel
- type: text
text: 26 de agosto a las 3:00 p.m.
responses:
'200':
description: Plantilla enviada o solicitud duplicada ya aceptada.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/RelaySuccess'
- $ref: '#/components/schemas/DuplicateSuccess'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/BalanceExhausted'
'404':
$ref: '#/components/responses/ConversationNotFound'
'413':
$ref: '#/components/responses/PayloadTooLarge'
'429':
$ref: '#/components/responses/RateLimited'
'502':
$ref: '#/components/responses/UpstreamFailure'
webhooks:
messageReceived:
post:
operationId: receiveNahuiConnectMessage
summary: Evento enviado por Nahui al webhook configurado
security: []
description: >-
Verifica X-Nahui-Signature-256 sobre el cuerpo crudo antes de parsear.
El servidor debe responder antes de 15 segundos.
parameters:
- name: X-Nahui-Event
in: header
required: true
schema:
type: string
const: message.received
- name: X-Nahui-Delivery
in: header
required: true
schema:
type: string
pattern: '^ncd_.+'
- name: X-Nahui-Signature-256
in: header
required: true
schema:
type: string
pattern: '^sha256=[A-Za-z0-9_-]+$'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MessageReceivedEvent'
responses:
'200':
description: Evento aceptado, opcionalmente con respuesta inmediata.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponse'
'401':
description: Firma ausente o inválida.
components:
securitySchemes:
connectionApiKey:
type: http
scheme: bearer
bearerFormat: nhc_...
description: API key de una sola conexión, generada en Nahui Connect.
parameters:
ConnectionId:
name: connectionId
in: path
required: true
schema:
type: string
pattern: '^ncc_.+'
description: ID de la conexión propietaria de la API key.
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 8
maxLength: 200
pattern: '^[A-Za-z0-9._:-]{8,200}$'
description: >-
Identificador único del envío lógico. Reutilizar únicamente al
reintentar el mismo envío.
schemas:
ReplyRequest:
type: object
additionalProperties: false
required: [conversationId, message]
properties:
conversationId:
$ref: '#/components/schemas/ConversationId'
message:
type: object
additionalProperties: false
required: [text]
properties:
text:
type: string
minLength: 1
maxLength: 4000
TemplateRequest:
type: object
additionalProperties: false
required: [conversationId, templateName]
properties:
conversationId:
$ref: '#/components/schemas/ConversationId'
templateName:
type: string
minLength: 3
maxLength: 512
pattern: '^[a-z0-9_]{3,512}$'
language:
type: string
minLength: 1
maxLength: 30
default: es_MX
components:
type: array
maxItems: 20
items:
type: object
additionalProperties: true
ConversationId:
type: string
pattern: '^ncv_.+'
maxLength: 160
Delivery:
type: object
additionalProperties: false
required: [externalMessageId, status]
properties:
externalMessageId:
type: [string, 'null']
status:
type: string
const: sent
RelaySuccess:
type: object
additionalProperties: false
required: [success, conversationId, delivery]
properties:
success:
type: boolean
const: true
conversationId:
$ref: '#/components/schemas/ConversationId'
delivery:
$ref: '#/components/schemas/Delivery'
DuplicateSuccess:
type: object
additionalProperties: false
required: [success, duplicate]
properties:
success:
type: boolean
const: true
duplicate:
type: boolean
const: true
Error:
type: object
required: [error]
properties:
error:
type: string
code:
type: string
WebhookResponse:
type: object
additionalProperties: false
properties:
reply:
type: object
additionalProperties: false
required: [text]
properties:
text:
type: string
minLength: 1
maxLength: 4000
MessageReceivedEvent:
type: object
additionalProperties: false
required:
- id
- type
- deliveryId
- projectId
- connectionId
- conversationId
- channel
- contact
- message
- receivedAt
properties:
id:
type: string
pattern: '^nce_.+'
type:
type: string
const: message.received
deliveryId:
type: string
pattern: '^ncd_.+'
projectId:
type: string
pattern: '^ncp_.+'
connectionId:
type: string
pattern: '^ncc_.+'
conversationId:
$ref: '#/components/schemas/ConversationId'
channel:
type: string
const: whatsapp
contact:
type: object
additionalProperties: false
required: [id, displayName]
properties:
id:
type: string
displayName:
type: string
message:
type: object
required: [id, type, timestamp]
properties:
id:
type: string
type:
type: string
timestamp:
type: string
format: date-time
text:
type: string
maxLength: 4000
button:
type: string
interactive:
type: object
media:
type: object
receivedAt:
type: string
format: date-time
responses:
BadRequest:
description: Solicitud inválida o destinatario prohibido.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Credencial ausente o inválida.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BalanceExhausted:
description: Sin mensajes incluidos ni saldo de recarga.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: No quedan mensajes incluidos ni saldo de recargas.
code: CONNECT_MESSAGE_BALANCE_EXHAUSTED
ConversationNotFound:
description: Conversación inexistente, bloqueada o no elegible.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PayloadTooLarge:
description: Cuerpo superior al límite permitido.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: Límite temporal alcanzado. Respetar Retry-After cuando exista.
headers:
Retry-After:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
UpstreamFailure:
description: Meta no pudo completar el envío.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'