API Reference

Referencia completa de la API de Rivelko / Complete Rivelko API reference

Base URL: https://app.rivelko.com

All request and response bodies use JSON (Content-Type: application/json) unless noted otherwise.

All authenticated endpoints accept either an active NextAuth session (cookie) or an API key (Authorization: Bearer rvk_...). See Authentication for details.

Todos los endpoints autenticados aceptan una sesion activa de NextAuth (cookie) o una API key (Authorization: Bearer rvk_...). Consulta Authentication para mas detalles.

This reference is organized API-first:

  • Management API — authenticated endpoints (API key or session) for building integrations. This is the public API.
  • Widget endpoints — unauthenticated endpoints used by the embeddable widget, validated by the Origin header.
  • Internal endpoints — first-party dashboard endpoints that are not part of the public API.

Table of Contents


Forms

All form management endpoints require authentication (session cookie or API key).

Todos los endpoints de gestion de formularios requieren autenticacion.


List Forms

List all forms for the authenticated user. Paginated.

GET/api/forms
curl -X GET "https://app.rivelko.com/api/forms?page=1&limit=10" \
  -H "Authorization: Bearer rvk_..."

Query Parameters:

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber10Items per page (max 100)
searchstring-Filter by form name (case-insensitive)
isPublishedstring-"true" or "false"

Response 200 OK:

json
{
  "forms": [
    {
      "id": "clx...",
      "name": "Contact Form",
      "isPublished": true,
      "formType": "CONVERSATIONAL",
      "createdAt": "2026-01-15T10:00:00.000Z",
      "_count": { "conversations": 42 }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 3,
    "totalPages": 1
  }
}

Create Form

Create a new form.

POST/api/forms
curl -X POST "https://app.rivelko.com/api/forms" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Contact Form",
  "welcomeMessage": "Hello! How can we help?",
  "goodbyeMessage": "Thanks for reaching out!",
  "tone": "FRIENDLY",
  "formType": "CONVERSATIONAL",
  "language": "EN",
  "allowedDomains": [
    "example.com"
  ],
  "questions": [
    {
      "order": 1,
      "text": "What is your name?",
      "type": "text",
      "required": true
    },
    {
      "order": 2,
      "text": "Email",
      "type": "email",
      "required": true
    }
  ]
}'

Request Body:

FieldTypeRequiredDefaultDescription
namestringYes-Form name (1-200 chars)
businessDescriptionstringNonullBusiness description
welcomeMessagestringYes-Welcome message (1-500 chars)
goodbyeMessagestringYes-Goodbye message (1-500 chars)
tonestringNo"FRIENDLY"FORMAL, CASUAL, or FRIENDLY
questionsQuestion[]Yes-Array of questions (min 1)
styleStyleNodefaultsWidget style options
languagestringNo"ES"ES or EN
formTypestringNo"CONVERSATIONAL"CONVERSATIONAL or CLASSIC
allowedDomainsstring[]No[]Domains allowed to use the widget

Question object:

FieldTypeRequiredDescription
ordernumberYesDisplay order
textstringYesQuestion text
typestringYestext, email, phone, number, select, date
requiredbooleanYesWhether the question is required
optionsstring[]NoOptions for select type

Style object:

FieldTypeDefaultDescription
primaryColorstring"#E11D62"Hex color for widget accent
avatarUrlstringnullAvatar image URL
positionstring"bottom-right"bottom-right or bottom-left
bubbleIconstringnullCustom bubble icon

Response 201 Created: The created form object.

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
403Plan limitForm limit reached for your plan

Get Form

Get a single form by ID. Must be the form owner.

GET/api/forms/:id
curl -X GET "https://app.rivelko.com/api/forms/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK: Full form object.

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Form not foundNot found or not owner

Update Form

Update a form. All fields are optional (partial update). Must be the form owner.

PUT/api/forms/:id
curl -X PUT "https://app.rivelko.com/api/forms/:id" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Updated Contact Form",
  "isPublished": true
}'

Request Body: Same fields as POST /api/forms, but all optional. Additionally:

FieldTypeDescription
isPublishedbooleanPublish or unpublish

Response 200 OK: Updated form object.


Delete Form

Delete a form and all its conversations. Must be the form owner.

DELETE/api/forms/:id
curl -X DELETE "https://app.rivelko.com/api/forms/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{ "success": true }

Generate Form with AI

Use AI to generate form questions from a business description or URL.

Genera preguntas de formulario usando IA a partir de una descripcion o URL.

POST/api/forms/generate
curl -X POST "https://app.rivelko.com/api/forms/generate" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "description": "A dental clinic in Madrid that does orthodontics and implants",
  "language": "ES"
}'

Request Body:

FieldTypeRequiredDescription
descriptionstringYes (if no url)Business description
urlstringYes (if no description)Website URL to analyze
languagestringNoES (default) or EN

Response 200 OK: Generated form structure (name, welcomeMessage, goodbyeMessage, questions, etc.). This is not persisted -- use POST /api/forms to create it.

Errors:

StatusErrorCause
400Either description or url...Neither field provided
500Failed to generate formAI generation error

Conversations

Endpoints for viewing form responses. Authenticated, paginated.

Endpoints para ver las respuestas de formularios. Autenticados y paginados.


List Conversations

List conversations across all forms owned by the user.

GET/api/conversations
curl -X GET "https://app.rivelko.com/api/conversations?page=1&limit=10" \
  -H "Authorization: Bearer rvk_..."

Query Parameters:

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber10Items per page (max 100)
formIdstring-Filter by form ID
statusstring-ACTIVE, COMPLETED, or ABANDONED
fromstring-Start date (ISO 8601)
tostring-End date (ISO 8601)

Response 200 OK:

json
{
  "conversations": [
    {
      "id": "clx...",
      "formId": "clx...",
      "status": "COMPLETED",
      "collectedData": { "name": "Juan", "email": "juan@example.com" },
      "createdAt": "2026-04-01T14:30:00.000Z",
      "form": { "id": "clx...", "name": "Contact Form", "formType": "CONVERSATIONAL" },
      "_count": { "messages": 8 }
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 42, "totalPages": 5 }
}

Get Conversation

Get a single conversation with paginated messages. Must own the form.

GET/api/conversations/:id
curl -X GET "https://app.rivelko.com/api/conversations/:id?messagesPage=1&messagesLimit=10" \
  -H "Authorization: Bearer rvk_..."

Query Parameters:

ParamTypeDefaultDescription
messagesPagenumber1Messages page number
messagesLimitnumber10Messages per page (max 100)

Response 200 OK:

json
{
  "id": "clx...",
  "formId": "clx...",
  "status": "COMPLETED",
  "collectedData": { "name": "Juan" },
  "form": { "id": "clx...", "name": "Contact Form", "questions": [...], "formType": "CONVERSATIONAL" },
  "messages": [
    { "id": "clx...", "role": "ASSISTANT", "content": "Hello! What is your name?", "createdAt": "..." },
    { "id": "clx...", "role": "VISITOR", "content": "Juan", "createdAt": "..." }
  ],
  "messagesPagination": { "page": 1, "limit": 10, "total": 8, "totalPages": 1 }
}

Errors:

StatusErrorCause
403ForbiddenNot the form owner
404Conversation not foundDoes not exist

Webhooks

Configure webhook notifications for form events. Requires PRO or BUSINESS plan.

Configura notificaciones webhook para eventos de formularios. Requiere plan PRO o BUSINESS.

For a complete guide, see Webhook Guide.


Get Webhook

Get the webhook configuration for a form. The secret is never returned; hasSecret indicates if one is set.

GET/api/forms/:id/webhook
curl -X GET "https://app.rivelko.com/api/forms/:id/webhook" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{
  "id": "clx...",
  "formId": "clx...",
  "url": "https://myapp.com/webhooks/rivelko",
  "events": ["conversation.completed"],
  "isActive": true,
  "hasSecret": true,
  "createdAt": "2026-04-01T00:00:00.000Z",
  "updatedAt": "2026-04-01T00:00:00.000Z"
}

Returns null if no webhook is configured.


Update Webhook

Create or update the webhook configuration for a form.

PUT/api/forms/:id/webhook
curl -X PUT "https://app.rivelko.com/api/forms/:id/webhook" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://myapp.com/webhooks/rivelko",
  "events": [
    "conversation.completed"
  ],
  "secret": "whsec_my_secret_key"
}'

Request Body:

FieldTypeRequiredDescription
urlstringYes (on create)Webhook endpoint URL
eventsstring[]Yes (on create)Events to listen for
isActivebooleanNoEnable/disable (default: true)
secretstringNoHMAC-SHA256 signing secret

Available events: conversation.completed

Response 201 Created (new) or 200 OK (update):

json
{
  "id": "clx...",
  "formId": "clx...",
  "url": "https://myapp.com/webhooks/rivelko",
  "events": ["conversation.completed"],
  "isActive": true,
  "hasSecret": true,
  "createdAt": "...",
  "updatedAt": "..."
}

Test Webhook

Send a test webhook to the configured URL. Useful for verifying your endpoint works.

POST/api/forms/:id/webhook/test
curl -X POST "https://app.rivelko.com/api/forms/:id/webhook/test" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{
  "success": true,
  "statusCode": 200
}

Errors:

StatusErrorCause
404No webhook configured for this formNo webhook config exists
502Webhook delivery failedEndpoint returned non-2xx or timed out

Pipelines

CRM pipelines for tracking deals through customizable stages. Creating a pipeline requires the PRO or BUSINESS plan (FREE has no pipeline access). Reading, updating, and deleting an existing pipeline works regardless of the current plan.

Pipelines CRM para seguir deals a traves de etapas personalizables. Crear un pipeline requiere plan PRO o BUSINESS (FREE no tiene acceso a pipelines). Leer, actualizar y borrar un pipeline existente funciona en cualquier plan.


List Pipelines

List all pipelines owned by the authenticated user, each with its stages. Not paginated -- always returns the full list.

GET/api/pipelines
curl -X GET "https://app.rivelko.com/api/pipelines" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{
  "pipelines": [
    {
      "id": "clx...",
      "userId": "clx...",
      "name": "Sales Pipeline",
      "createdAt": "2026-04-01T00:00:00.000Z",
      "updatedAt": "2026-04-01T00:00:00.000Z",
      "stages": [
        { "id": "clx...", "pipelineId": "clx...", "name": "New Lead", "color": "#3b82f6", "order": 0 },
        { "id": "clx...", "pipelineId": "clx...", "name": "Contacted", "color": "#8b5cf6", "order": 1 }
      ]
    }
  ]
}

Errors:

StatusErrorCause
401UnauthorizedNot authenticated

Create Pipeline

Create a new pipeline. Requires the PRO or BUSINESS plan.

Crea un pipeline nuevo. Requiere plan PRO o BUSINESS.

POST/api/pipelines
curl -X POST "https://app.rivelko.com/api/pipelines" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sales Pipeline",
  "stages": [
    {
      "name": "New Lead",
      "color": "#3b82f6",
      "order": 0
    },
    {
      "name": "Contacted",
      "color": "#8b5cf6",
      "order": 1
    }
  ]
}'

Request Body:

FieldTypeRequiredDefaultDescription
namestringYes-Pipeline name (1-100 chars)
stagesStage[]Nosee belowCustom stages (2-10 if provided)

Stage object:

FieldTypeRequiredDefaultDescription
namestringYes-Stage name (1-50 chars)
colorstringNo"#6b7280"Hex color (#RRGGBB)
ordernumberYes-Display order (integer >= 0)

If stages is omitted, the pipeline is created with 6 built-in default stages: New Lead, Contacted, Qualified, Proposal, Won, Lost.

Response 201 Created: The created pipeline object with its stages (same shape as a GET /api/pipelines item).

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
403Plan limit reached: maximum 0 pipeline (code: "FEATURE_NOT_AVAILABLE")Plan doesn't include pipelines (FREE)
403Plan limit reached: maximum {N} pipelines (code: "PIPELINE_LIMIT_REACHED")Plan's pipeline count reached (PRO: 3; unlimited on BUSINESS)

Get Pipeline

Get a single pipeline with its stages. Must be the pipeline owner.

GET/api/pipelines/:id
curl -X GET "https://app.rivelko.com/api/pipelines/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK: Pipeline object (same shape as a list item).

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Pipeline with id "{id}" not foundNot found or not owner

Update Pipeline

Update a pipeline's name and/or stages. Must be the pipeline owner. No plan check is re-applied on update -- it works on any plan as long as you already own the pipeline.

PUT/api/pipelines/:id
curl -X PUT "https://app.rivelko.com/api/pipelines/:id" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sales Pipeline (renamed)",
  "stages": [
    {
      "name": "New Lead",
      "color": "#3b82f6",
      "order": 0
    },
    {
      "name": "Won",
      "color": "#22c55e",
      "order": 1
    }
  ]
}'

Request Body:

FieldTypeRequiredDescription
namestringNoNew pipeline name (1-100 chars)
stagesStage[]NoFull replacement stage list (2-10)

Stage object:

FieldTypeRequiredDescription
idstringNoAccepted by validation but ignored -- see note below
namestringYesStage name (1-50 chars)
colorstringNoHex color (#RRGGBB), default "#6b7280"
ordernumberYesDisplay order (integer >= 0)

Note: If stages is provided, the pipeline's entire stage list is deleted and recreated from the array you send -- it is not a partial merge, and new stage IDs are always generated (any id you include per stage is validated but has no effect on the update). If any existing deal still points at a stage ID that would be deleted, the request fails at the database level (the Deal.stageId foreign key is ON DELETE RESTRICT), surfacing as a generic 500 Internal server error. Move or delete affected deals first (PATCH /api/deals/:id/move) before replacing stages that are in use.

Response 200 OK: Updated pipeline object with its stages.

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
404Pipeline with id "{id}" not foundNot found or not owner
500Internal server errorReplacing stages while a deal still references one of the removed stage IDs

Delete Pipeline

Delete a pipeline. Must be the pipeline owner. Cascades: also deletes all of the pipeline's stages and deals.

DELETE/api/pipelines/:id
curl -X DELETE "https://app.rivelko.com/api/pipelines/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{ "success": true }

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Pipeline with id "{id}" not foundNot found or not owner

Deals

Deals track potential sales moving through a pipeline's stages. Every deal endpoint requires ownership of the parent pipeline; there is no separate authorization check on the deal itself.

Los deals siguen ventas potenciales a traves de las etapas de un pipeline. Todos los endpoints de deals requieren ser propietario del pipeline padre.

Note: Creating a deal does not re-check the pipeline feature flag or enforce a monthly count, even though maxDealsPerMonth is defined per plan (PRO: 500/month, BUSINESS: unlimited) and a DEAL_LIMIT_REACHED error code exists in the error catalog. Neither is currently applied by this endpoint -- deal creation is effectively unlimited once you own a pipeline.


List Deals

List deals in the pipeline identified by :id. Must be the pipeline owner.

GET/api/pipelines/:id/deals
curl -X GET "https://app.rivelko.com/api/pipelines/:id/deals" \
  -H "Authorization: Bearer rvk_..."

Query Parameters:

ParamTypeDefaultDescription
stageIdstring-Filter by stage ID
searchstring-Case-insensitive match on title, contact name, or contact email

Note: page and limit are accepted and validated by this endpoint's query schema, but they have no effect -- the response always contains the full, unpaginated list of deals for the pipeline. If the query string fails validation entirely, the endpoint silently falls back to the defaults instead of returning 400.

Response 200 OK:

json
{
  "deals": [
    {
      "id": "clx...",
      "pipelineId": "clx...",
      "stageId": "clx...",
      "conversationId": null,
      "title": "Juan Perez",
      "value": 1500,
      "currency": "USD",
      "contactName": "Juan Perez",
      "contactEmail": "juan@example.com",
      "notes": null,
      "createdAt": "2026-04-01T00:00:00.000Z",
      "updatedAt": "2026-04-01T00:00:00.000Z",
      "stage": { "id": "clx...", "pipelineId": "clx...", "name": "New Lead", "color": "#3b82f6", "order": 0 }
    }
  ]
}

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Pipeline not foundPipeline does not exist or you don't own it

Create Deal

Create a deal in the pipeline identified by :id. Must be the pipeline owner.

POST/api/pipelines/:id/deals
curl -X POST "https://app.rivelko.com/api/pipelines/:id/deals" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "pipelineId": "YOUR_PIPELINE_ID",
  "stageId": "YOUR_STAGE_ID",
  "title": "Juan Perez",
  "value": 1500,
  "currency": "USD",
  "contactName": "Juan Perez",
  "contactEmail": "juan@example.com"
}'

Request Body:

FieldTypeRequiredDefaultDescription
pipelineIdstringYes-Must be present for validation to pass, but the path :id always wins -- send the same ID in both places
stageIdstringYes-Must be a stage belonging to this pipeline
titlestringYes-Deal title (1-200 chars)
valuenumberNonullDeal amount (>= 0)
currencystringNo"USD"3-character currency code (e.g. USD, EUR)
contactNamestringNonullContact name (max 200 chars)
contactEmailstringNonullContact email (validated format)
notesstringNonullFree-text notes (max 5000 chars)

Response 201 Created: The created deal object (no nested stage; see GET /api/deals/:id for the field list).

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
404Pipeline with id "{id}" not foundPipeline does not exist or you don't own it

Get Deal

Get a single deal, including its stage. Must own the parent pipeline.

GET/api/deals/:id
curl -X GET "https://app.rivelko.com/api/deals/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK: Deal object with a nested stage (same shape as a GET /api/pipelines/:id/deals item).

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Deal not foundDeal does not exist, or its pipeline isn't yours

Update Deal

Update a deal. Must own the parent pipeline. All fields optional.

PUT/api/deals/:id
curl -X PUT "https://app.rivelko.com/api/deals/:id" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Juan Perez (updated)",
  "value": 2000
}'

Request Body:

FieldTypeDescription
stageIdstringMove to a different stage (should be in the same pipeline)
titlestringDeal title (1-200 chars)
valuenumberDeal amount (>= 0)
currencystring3-character currency code
contactNamestringContact name (max 200 chars)
contactEmailstringContact email (validated format)
notesstringFree-text notes (max 5000 chars)

Response 200 OK: Updated deal object.

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
404Deal with id "{id}" not foundDeal does not exist
404Pipeline with id "{id}" not foundDeal exists, but its pipeline isn't yours

Delete Deal

Delete a deal. Must own the parent pipeline.

DELETE/api/deals/:id
curl -X DELETE "https://app.rivelko.com/api/deals/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{ "success": true }

Errors: Same as PUT /api/deals/:id (Deal ... not found / Pipeline ... not found).


Move Deal

Move a deal to a different stage. Equivalent to PUT /api/deals/:id with only stageId.

PATCH/api/deals/:id/move
curl -X PATCH "https://app.rivelko.com/api/deals/:id/move" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "stageId": "YOUR_STAGE_ID"
}'

Request Body:

FieldTypeRequiredDescription
stageIdstringYesTarget stage ID

Response 200 OK: Updated deal object.

Errors: Same as PUT /api/deals/:id.


Tickets

Support tickets, optionally linked to the form/conversation they originated from. Creating a ticket requires the PRO or BUSINESS plan (FREE has no ticket access). Reading, updating, and deleting an existing ticket works regardless of the current plan.

Tickets de soporte, opcionalmente vinculados al formulario/conversacion de origen. Crear un ticket requiere plan PRO o BUSINESS (FREE no tiene acceso a tickets). Leer, actualizar y borrar un ticket existente funciona en cualquier plan.


List Tickets

List tickets owned by the authenticated user. Not paginated -- always returns the full list (no page/limit params exist for this endpoint).

GET/api/tickets
curl -X GET "https://app.rivelko.com/api/tickets" \
  -H "Authorization: Bearer rvk_..."

Query Parameters:

ParamTypeDefaultDescription
statusstring-OPEN, IN_PROGRESS, RESOLVED, or CLOSED
prioritystring-LOW, MEDIUM, HIGH, or URGENT
searchstring-Case-insensitive match on subject, contact name, or contact email

Response 200 OK:

json
{
  "tickets": [
    {
      "id": "clx...",
      "userId": "clx...",
      "number": 1000,
      "conversationId": null,
      "formId": null,
      "subject": "Cannot log in",
      "description": "I get an error when I try to sign in.",
      "priority": "HIGH",
      "status": "OPEN",
      "assigneeName": null,
      "assigneeEmail": null,
      "contactName": "Juan Perez",
      "contactEmail": "juan@example.com",
      "createdAt": "2026-04-01T00:00:00.000Z",
      "updatedAt": "2026-04-01T00:00:00.000Z"
    }
  ]
}

Errors:

StatusErrorCause
400Invalid queryQuery params fail validation
401UnauthorizedNot authenticated

Create Ticket

Create a ticket. Requires the PRO or BUSINESS plan.

POST/api/tickets
curl -X POST "https://app.rivelko.com/api/tickets" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "subject": "Cannot log in",
  "description": "I get an error when I try to sign in.",
  "priority": "HIGH",
  "contactName": "Juan Perez",
  "contactEmail": "juan@example.com"
}'

Request Body:

FieldTypeRequiredDefaultDescription
subjectstringYes-Ticket subject (1-300 chars)
descriptionstringNonullFree-text description (max 5000 chars)
prioritystringNo"MEDIUM"LOW, MEDIUM, HIGH, or URGENT
contactNamestringNonullContact name (max 200 chars)
contactEmailstringNonullContact email (validated format)

number is assigned automatically (sequential per user, starting at 1000). status always starts as "OPEN". formId and conversationId cannot be set through this endpoint -- they're only populated internally when a ticket is auto-created from a form conversation.

Response 201 Created: The created ticket object.

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
403Plan limit reached: maximum 0 tickets (code: "FEATURE_NOT_AVAILABLE")Plan doesn't include tickets (FREE)

Note: maxTicketsPerMonth is defined per plan (PRO: 500/month, BUSINESS: unlimited) but is not currently enforced by this endpoint -- only the tickets feature flag is checked, so ticket creation is effectively unlimited on PRO/BUSINESS.


Get Ticket

Get a single ticket. Must be the ticket owner.

GET/api/tickets/:id
curl -X GET "https://app.rivelko.com/api/tickets/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK: Ticket object.

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Ticket with id "{id}" not foundNot found or not owner

Update Ticket

Update a ticket. Must be the ticket owner. All fields optional.

PUT/api/tickets/:id
curl -X PUT "https://app.rivelko.com/api/tickets/:id" \
  -H "Authorization: Bearer rvk_..." \
  -H "Content-Type: application/json" \
  -d '{
  "status": "RESOLVED",
  "assigneeName": "Support Agent"
}'

Request Body:

FieldTypeDescription
subjectstringTicket subject (1-300 chars)
descriptionstringFree-text description (max 5000 chars)
prioritystringLOW, MEDIUM, HIGH, or URGENT
statusstringOPEN, IN_PROGRESS, RESOLVED, or CLOSED
assigneeNamestringAssignee name (max 200 chars)
assigneeEmailstringAssignee email (validated format)
contactNamestringContact name (max 200 chars)
contactEmailstringContact email (validated format)

Response 200 OK: Updated ticket object.

Errors:

StatusErrorCause
400Invalid dataValidation failure
401UnauthorizedNot authenticated
404Ticket with id "{id}" not foundNot found or not owner

Delete Ticket

Delete a ticket. Must be the ticket owner.

DELETE/api/tickets/:id
curl -X DELETE "https://app.rivelko.com/api/tickets/:id" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{ "success": true }

Errors:

StatusErrorCause
401UnauthorizedNot authenticated
404Ticket with id "{id}" not foundNot found or not owner

Account

Your profile, API key and current usage.


Get Current User

Get the authenticated user's profile. API key is returned masked (rvk_...xxxx).

GET/api/users/me
curl -X GET "https://app.rivelko.com/api/users/me" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{
  "id": "clx...",
  "name": "Juan Perez",
  "email": "juan@example.com",
  "image": "https://...",
  "plan": "PRO",
  "apiKey": "rvk_...a1b2",
  "createdAt": "2026-01-01T00:00:00.000Z"
}

Rotate API Key

Generate a new API key. Replaces the previous key. The full key is only returned once.

Genera una nueva API key. Reemplaza la anterior. La clave completa solo se muestra una vez.

POST/api/users/me/api-key
curl -X POST "https://app.rivelko.com/api/users/me/api-key" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK:

json
{
  "apiKey": "rvk_a1b2c3d4e5f6...64_hex_chars"
}

Note: The returned key is immediately usable as Authorization: Bearer rvk_... on any management endpoint. See Authentication.


Get Usage

Get current month's usage vs plan limits.

GET/api/usage
curl -X GET "https://app.rivelko.com/api/usage" \
  -H "Authorization: Bearer rvk_..."

Response 200 OK: Plan-specific usage object with current counts and limits.


Widget endpoints

These endpoints require no authentication. They are used by the embeddable widget and can be called from any client. Domain validation is enforced via the Origin or Referer header.

Estos endpoints no requieren autenticacion. Son usados por el widget embebible y validados por dominio.


Get Form Config

Fetch the configuration of a published form. Used by the widget on initialization.

GET/api/forms/:id/config
curl -X GET "https://app.rivelko.com/api/forms/:id/config"

Parameters:

NameInTypeRequiredDescription
idpathstringYesForm ID

CORS: This endpoint is callable from any origin. It responds with Access-Control-Allow-Origin: * and implements OPTIONS for CORS preflight requests.

Response 200 OK:

json
{
  "id": "clx...",
  "name": "Dental Clinic Contact Form",
  "welcomeMessage": "Hi! Tell me about your visit.",
  "goodbyeMessage": "Thanks! We'll be in touch.",
  "tone": "FRIENDLY",
  "questions": [
    {
      "order": 1,
      "text": "What is your name?",
      "type": "text",
      "required": true,
      "options": null
    }
  ],
  "style": {
    "primaryColor": "#E11D62",
    "avatarUrl": null,
    "position": "bottom-right",
    "bubbleIcon": null
  },
  "language": "ES",
  "formType": "CONVERSATIONAL",
  "allowedDomains": ["example.com"],
  "objective": "Collect leads for a dental consultation",
  "showBranding": true
}

objective is the business objective used to steer the AI (may be null). showBranding indicates whether the "Powered by Rivelko" badge must be shown -- always true on the FREE plan, otherwise it follows the form's style setting.

Errors:

StatusErrorCause
404Form with id "{id}" not foundForm does not exist or is not published
403Account inactive (code: "SUBSCRIPTION_INACTIVE")Owner's subscription is not active (paid plans)

Send Chat Message

Send a message to a conversational form. Returns a streaming text response (chunked transfer encoding).

Envia un mensaje a un formulario conversacional. Devuelve una respuesta en streaming.

POST/api/chat
curl -X POST "https://app.rivelko.com/api/chat" \
  -H "Origin: https://example.com" \
  -H "Content-Type: application/json" \
  -d '{
  "formId": "YOUR_FORM_ID",
  "message": "Hello"
}'

Para continuar una conversación existente, reenvía el conversationId devuelto en la cabecera X-Conversation-Id:

json
{ "formId": "YOUR_FORM_ID", "conversationId": "CONV_ID", "message": "Me llamo Juan" }

Headers:

HeaderValueRequired
Content-Typeapplication/jsonYes
OriginYour domain originYes

Request Body:

FieldTypeRequiredDescription
formIdstringYesForm ID
conversationIdstringNoConversation ID (omit for first message)
messagestringYesVisitor message (1-5000 chars)

Response 200 OK:

  • Content-Type: text/plain; charset=utf-8
  • Body: Streaming text (AI assistant response, chunked)
  • Header X-Conversation-Id: The conversation ID (use for subsequent messages)

CORS Headers:

Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Conversation-Id

Errors:

StatusErrorCause
400Invalid dataValidation failure (missing/invalid fields)
400Classic forms do not support chatForm type is CLASSIC
403Domain not allowedOrigin not in form's allowedDomains
404Form not foundForm does not exist or is not published
404Conversation not foundInvalid conversationId for this form

JavaScript Example (streaming):

javascript
const response = await fetch("https://app.rivelko.com/api/chat", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ formId: "YOUR_FORM_ID", message: "Hello" }),
});

const conversationId = response.headers.get("X-Conversation-Id");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  fullText += decoder.decode(value, { stream: true });
  console.log(fullText); // progressive update
}

Submit Form Response

Submit a classic form with all answers at once.

Envia las respuestas de un formulario clasico.

POST/api/forms/:id/submit
curl -X POST "https://app.rivelko.com/api/forms/:id/submit" \
  -H "Origin: https://example.com" \
  -H "Content-Type: application/json" \
  -d '{
  "answers": {
    "What is your name?": "Juan Perez",
    "What is your email?": "juan@example.com"
  }
}'

Parameters:

NameInTypeRequiredDescription
idpathstringYesForm ID

Headers:

HeaderValueRequired
Content-Typeapplication/jsonYes
OriginYour domain originYes

Request Body:

FieldTypeRequiredDescription
answersRecord<string, string>YesMap of question text to answer
json
{
  "answers": {
    "What is your name?": "Juan Perez",
    "What is your email?": "juan@example.com",
    "What is your phone?": "+34 912 345 678"
  }
}

Response 200 OK:

json
{
  "success": true,
  "conversationId": "clx..."
}

Errors:

StatusErrorCause
400Invalid dataValidation failure
400This endpoint is only for classic formsForm type is CONVERSATIONAL
400Missing required field: {question}Required question not answered
403Domain not allowedOrigin not in allowedDomains
404Form not foundForm does not exist or is not published

Get Widget Script

Serves the embeddable widget JavaScript bundle (~28KB).

GET/api/widget
curl -X GET "https://app.rivelko.com/api/widget"

Response: application/javascript

Cached for 1 hour in production, no cache in development. You typically do not call this directly; use the <script> embed tag instead. See Widget Integration.


Internal endpoints

These endpoints are not part of the public API. They exist for the Rivelko dashboard (first-party UI) and offer no stability guarantee — do not call them from external integrations; they may change or be removed without notice. Listed here only for completeness.

Estos endpoints no forman parte de la API publica. Existen para el panel de Rivelko y no ofrecen garantia de estabilidad; no deben llamarse desde integraciones externas.


Create Checkout Session

POST /api/stripe/checkout — Creates a Stripe Checkout session and returns a checkout.stripe.com URL to redirect a signed-in user in the browser. Meaningless to a headless integration (there is no browser to redirect), so it is not part of the public API.


Open Billing Portal

POST /api/stripe/portal — Creates a Stripe Customer Portal session and returns a billing.stripe.com URL to redirect the user. First-party billing management only.


Get Dashboard Stats

GET /api/dashboard/stats — Returns overview counters shaped specifically for the dashboard UI (recentConversations, completionRate, …). For programmatic usage data use Get Usage instead.


Update Current User

PUT /api/users/me — Updates the signed-in user's display name. A settings-panel action; not part of the integration surface.