VitrinaAPI

Knowledge base — sources and indexed text

The agent's hand-authored sources — a problem, its root cause and the solution steps, written by hand instead of uploaded — and a library file's indexed text: literally what the agent searches.

Beta
This may change at any time, with a changelog entry and a notice to recent callers — see versioning.

Download the full API pública projection: openapi.json.

Give the agent something to quote explains this resource in prose, with runnable examples.

MethodPathWhat it does
GET/kb-files/{id}/textFull extracted (indexed) text of a knowledge file
POST/kb/rollup/refreshRefresh KB chunk hits rollup
GET/kb/sourcesList KB sources
POST/kb/sourcesCreate a KB source
DELETE/kb/sources/{id}Delete a KB source
GET/kb/sources/{id}Get KB source
PUT/kb/sources/{id}Update a KB source
POST/kb/sources/{id}/embedEmbed manual KB entry into documentation_chunks
POST/kb/sources/{id}/reingestRe-ingest KB source
POST/kb/sources/{id}/revokeRevoke KB source

GET /kb-files/{id}/text

Full extracted (indexed) text of a knowledge file

Every ingested chunk in document order — literally what search_knowledge_base searches. text is null when nothing is indexed yet.

ParameterInTypeRequiredConstraints
idpathstringyes
curl https://api.vitrinadev.com/api/v1/kb-files/<id>/text \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "text": "Todos los vehículos nuevos incluyen 3 años o 100.000 km de garantía de fábrica…"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /kb/rollup/refresh

Refresh KB chunk hits rollup

Refreshes the kb_chunk_hits materialised view — how often each chunk was actually retrieved and cited, behind the "most-used knowledge" reporting. Synchronous; safe to call repeatedly.

curl -X POST https://api.vitrinadev.com/api/v1/kb/rollup/refresh \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "refreshed": true
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /kb/sources

List KB sources

Every manual KB source in the workspace, newest first. status filters to active (approved + pending — everything not revoked), revoked, or all; omit it for active. tag filters to sources carrying that exact tag. Paginated with limit/offset (default 50, max 200).

ParameterInTypeRequiredConstraints
tenant_idqueryuuidno
statusqueryactive \revoked \all
tagquerystringnomín. 1
limitqueryintegerno≥ 1, ≤ 200, por defecto 50
offsetqueryinteger | nullno≥ 0, por defecto 0
curl https://api.vitrinadev.com/api/v1/kb/sources \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "a5a5a5a5-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "status": "approved",
      "tags": [
        "horarios",
        "atencion"
      ],
      "summary": {
        "title": "Horario de atención",
        "problem": "El cliente pregunta el horario de atención",
        "root_cause": null,
        "solution_steps": [
          "Confirmar el horario: lunes a viernes de 9:00 a 19:00",
          "Ofrecer agendar una cita si corresponde"
        ],
        "product": null,
        "language": "es",
        "notes": null
      },
      "chunk_ids": [
        3801
      ],
      "verified_until": null,
      "error": null,
      "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
      "ticket_id": null,
      "help_center_article_id": null,
      "help_center_locale": null,
      "help_center_url": null,
      "created_at": "2026-09-10T12:00:00.000Z",
      "updated_at": "2026-09-10T12:05:00.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50
    },
    "offset": 0
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /kb/sources

Create a KB source

Hand-author a KB entry: summary is the structured shape the agent quotes from (title, problem, optional root_cause, solution_steps — 1 to 50 steps), tags is 1 to 20 free-form labels, and verified_until optionally marks when the answer should be reviewed again.

The row is born status: "pending" and with NO chunks — creating it does not make it retrievable. Nothing is searchable until POST /kb/sources/\{id\}/embed runs (typically after review sets status: "approved" via PUT). Answers 201. Fires kb.source.create.

Body

FieldTypeRequiredConstraints
summaryobjectyes
tagsstring[]yes
verified_untilstring | null—date-time
curl -X POST https://api.vitrinadev.com/api/v1/kb/sources \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": {
      "title": "Horario de atención",
      "problem": "El cliente pregunta el horario de atención",
      "solution_steps": [
        "Confirmar el horario: lunes a viernes de 9:00 a 19:00",
        "Ofrecer agendar una cita si corresponde"
      ]
    },
    "tags": [
      "horarios",
      "atencion"
    ]
  }'

Example response (201)

{
  "data": {
    "id": "a5a5a5a5-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "status": "pending",
    "tags": [
      "horarios",
      "atencion"
    ],
    "summary": {
      "title": "Horario de atención",
      "problem": "El cliente pregunta el horario de atención",
      "root_cause": null,
      "solution_steps": [
        "Confirmar el horario: lunes a viernes de 9:00 a 19:00",
        "Ofrecer agendar una cita si corresponde"
      ],
      "product": null,
      "language": "es",
      "notes": null
    },
    "chunk_ids": [],
    "verified_until": null,
    "error": null,
    "conversation_id": null,
    "ticket_id": null,
    "help_center_article_id": null,
    "help_center_locale": null,
    "help_center_url": null,
    "created_at": "2026-09-10T12:00:00.000Z",
    "updated_at": "2026-09-10T12:05:00.000Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

DELETE /kb/sources/{id}

Delete a KB source

A HARD delete — unlike DELETE /kb-files/\{id\} (soft), the row and its embedded chunks are gone; retrieval can no longer return it and there is no un-delete. To stop the source being retrieved WITHOUT losing it, prefer POST /kb/sources/\{id\}/revoke (status: "revoked", row kept). Fires kb.source.delete. 204.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl -X DELETE https://api.vitrinadev.com/api/v1/kb/sources/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429

GET /kb/sources/{id}

Get KB source

The full row, including its summary and chunk_ids.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl https://api.vitrinadev.com/api/v1/kb/sources/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "a5a5a5a5-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "status": "approved",
    "tags": [
      "horarios",
      "atencion"
    ],
    "summary": {
      "title": "Horario de atención",
      "problem": "El cliente pregunta el horario de atención",
      "root_cause": null,
      "solution_steps": [
        "Confirmar el horario: lunes a viernes de 9:00 a 19:00",
        "Ofrecer agendar una cita si corresponde"
      ],
      "product": null,
      "language": "es",
      "notes": null
    },
    "chunk_ids": [
      3801
    ],
    "verified_until": null,
    "error": null,
    "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
    "ticket_id": null,
    "help_center_article_id": null,
    "help_center_locale": null,
    "help_center_url": null,
    "created_at": "2026-09-10T12:00:00.000Z",
    "updated_at": "2026-09-10T12:05:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

PUT /kb/sources/{id}

Update a KB source

Every field optional; at least one is required (an empty body is a 400). This is the review step: setting status: "approved" is what the manual-authoring flow uses to mark a draft entry ready — it does NOT re-embed by itself, so a status change alone does not update what the agent retrieves. Call POST /kb/sources/\{id\}/embed after, whether or not summary changed, to push the update into documentation_chunks. Fires kb.source.update.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1

Body

FieldTypeRequiredConstraints
summaryobject—
tagsstring[]—
statuspending \approved \revoked \
verified_untilstring | null—date-time
curl -X PUT https://api.vitrinadev.com/api/v1/kb/sources/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "approved"
  }'

Example response (200)

{
  "data": {
    "id": "a5a5a5a5-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "status": "approved",
    "tags": [
      "horarios",
      "atencion"
    ],
    "summary": {
      "title": "Horario de atención",
      "problem": "El cliente pregunta el horario de atención",
      "root_cause": null,
      "solution_steps": [
        "Confirmar el horario: lunes a viernes de 9:00 a 19:00",
        "Ofrecer agendar una cita si corresponde"
      ],
      "product": null,
      "language": "es",
      "notes": null
    },
    "chunk_ids": [
      3801
    ],
    "verified_until": null,
    "error": null,
    "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
    "ticket_id": null,
    "help_center_article_id": null,
    "help_center_locale": null,
    "help_center_url": null,
    "created_at": "2026-09-10T12:00:00.000Z",
    "updated_at": "2026-09-10T12:05:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /kb/sources/{id}/embed

Embed manual KB entry into documentation_chunks

Renders the source’s summary to markdown and (re-)embeds it, synchronously. Deletes any chunks this source previously produced before inserting the new ones, so it is safe to call repeatedly — a source never doubles up in retrieval. This is the ONLY thing that makes a manual entry (or an edit to one) actually retrievable; a source with no summary is a 400. Fires kb.source.embed.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl -X POST https://api.vitrinadev.com/api/v1/kb/sources/<id>/embed \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "kb_source_id": "a5a5a5a5-0000-4000-8000-000000000001",
    "chunks": 2,
    "chunk_ids": [
      3801,
      3802
    ]
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /kb/sources/{id}/reingest

Re-ingest KB source

Re-queues the source’s LINKED CONVERSATION through the automatic kb-ingestion worker (the same path a resolved ticket triggers) — for a source with no conversation_id (a purely manual entry) this is a 400; use POST /kb/sources/\{id\}/embed instead, which re-embeds the stored summary directly. Answers 202 — the work happens on the worker, not synchronously.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl -X POST https://api.vitrinadev.com/api/v1/kb/sources/<id>/reingest \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (202)

{
  "data": {
    "enqueued": true,
    "kb_source_id": "a5a5a5a5-0000-4000-8000-000000000001"
  }
}

Answers: 202 · 400 · 401 · 403 · 404 · 409 · 429

POST /kb/sources/{id}/revoke

Revoke KB source

The reversible alternative to delete: sets status: "revoked". The row and its chunks survive, but a revoked source is excluded from the default GET /kb/sources list (pass status=all to see it) — this endpoint does not itself purge documentation_chunks, so re-approve and re-embed to bring it back rather than re-creating it. 204.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl -X POST https://api.vitrinadev.com/api/v1/kb/sources/<id>/revoke \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429

On this page