Widget API
The widget API is a small set of endpoints under https://api.neofir.com/v1/widget/*. Auth is an X-Api-Key header carrying your org's widget key. Origin enforcement runs server-side against your allowed_domains list.
POST /widget/init
Resume or create a conversation; receive the org config.
POST /v1/widget/init
X-Api-Key: pk_live_...
Content-Type: application/json
{
"visitor_id": "uuid-or-null"
}
Response:
{
"org": {
"name": "Neofir",
"branding": {
"primary_color": "#a47d52",
"welcome_msg": "Salut. Neofir...",
"logo_url": null
},
"shortcuts": ["Prețuri", "Cum funcționează", "Integrări", "Discovery call"]
},
"conversation_id": "uuid",
"visitor_id": "uuid"
}
Rate limit: 10 / 5 min per IP.
POST /widget/message
Send a user message; receive a streamed reply.
POST /v1/widget/message
X-Api-Key: pk_live_...
Content-Type: application/json
{
"conversation_id": "uuid",
"visitor_id": "uuid",
"content": "How much does Studio cost?"
}
Response: Server-Sent Events. Each event is data: {...}\n\n.
data: {"type":"delta","content":"Studio is €45"}
data: {"type":"delta","content":"/month..."}
data: {"type":"final","content":"Studio is €45/month, 2,000 messages, all channels.","chips":["Pornim?"],"message_id":"uuid"}
Possible event types:
delta— incremental token from the LLM.tool_call— the model is invoking a tool. Includestoolname andargs.final— the complete reply (chips and message_id included).error— failure; includescodeandmessage.
Rate limits: 30 / min per visitor_id, 20 / min per org.
POST /widget/feedback
Record thumbs-up / thumbs-down on a message.
POST /v1/widget/feedback
X-Api-Key: pk_live_...
{
"message_id": "uuid",
"rating": "up" | "down",
"reason": "string optional"
}
GET /widget/availability
Returns the next N available slots for a given service id. Used by the inline slot picker; the agent also calls this internally via check_availability.
GET /v1/widget/availability?service_id=demo&from=2026-06-01&to=2026-06-07
X-Api-Key: pk_live_...
POST /widget/booking
Confirm a booking once the agent has gathered name, phone, slot, service.
POST /v1/widget/booking
X-Api-Key: pk_live_...
{
"conversation_id": "uuid",
"service_id": "demo",
"starts_at": "2026-06-02T10:00:00Z",
"customer_name": "Leo",
"customer_phone": "+40751051221",
"customer_email": "leo@example.com"
}
Error codes
| Code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR | 422 | Body did not match the Zod schema. |
RATE_LIMIT | 429 | You hit one of the rate windows. Retry after the Retry-After header. |
QUOTA_EXCEEDED | 402 | The org has used its monthly message quota. |
UNAUTHORIZED | 401 | Missing or invalid X-Api-Key. |
ORIGIN_NOT_ALLOWED | 403 | The request's Origin is not in the org's allowed_domains. |
NOT_FOUND | 404 | Conversation, service, or booking not found in this org. |
BOOKING_FAILED | 409 | Slot taken, outside hours, or constraint violation. |
INTERNAL | 500 | Backend failure. Logged with a request id you can quote in support. |