VitrinaAPI

Líneas de la clínica

La lista corta de lo que buscan los pacientes (Implantes, Estética, Endodoncia…): cada especialidad del equipo y cada anuncio pertenecen a una sola Línea, y cada lead lleva una.

Beta
Puede cambiar en cualquier momento, con una entrada en el changelog y aviso a quienes la llamaron recientemente — ver versionado.

Descarga la proyección completa de la API pública: openapi.json.

Definir prestaciones y precios explica este recurso en prosa, con ejemplos ejecutables.

MétodoRutaQué hace
GET/clinic/lineasList the clinic's Líneas, with the mapping and the unmapped
POST/clinic/lineasAdd a Línea
PATCH/clinic/lineas/{id}Rename or archive a Línea
POST/clinic/lineas/{id}/keywordsAdd a keyword for leads without an ad
POST/clinic/lineas/{id}/mergeMerge a Línea into another
POST/clinic/lineas/{id}/rulesMap ads to a Línea by a name fragment
DELETE/clinic/lineas/keywords/{id}Remove a keyword
DELETE/clinic/lineas/rules/{id}Remove a name fragment rule
PUT/clinic/lineas/specialties/{id}Place a roster specialty in a Línea

GET /clinic/lineas

List the clinic's Líneas, with the mapping and the unmapped

A Línea is one entry in the clinic's own short list of what patients come for (Implantes, Estética, Endodoncia, TTM…), usually fewer than its specialties. The list is seeded from the roster's specialties on first read and is editable. Every roster specialty and every ad maps into exactly one Línea: specialties and rules (name fragments an ad's campaign, ad set or ad name must contain) say how; keywords are the words a lead without an ad says when it wants the Línea (seeded for dental clinics, editable); unmapped_specialties and the ads with linea_id: null are the ones the owner still has to place. Each lead carries exactly one Línea (linea on the lead). Requires clinic:read.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

curl https://api.vitrinadev.com/api/v1/clinic/lineas \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "lineas": [
      {
        "id": "5b1f0c1e-0000-4000-8000-000000000001",
        "nombre": "Implantes",
        "is_default": false,
        "archived": false,
        "leads": 41,
        "open_leads": 33,
        "rules": [
          {
            "id": "5b1f0c1e-0000-4000-8000-000000000003",
            "match": "implant",
            "source": "seed"
          }
        ],
        "keywords": [
          {
            "id": "5b1f0c1e-0000-4000-8000-000000000005",
            "keyword": "implant",
            "source": "seed"
          }
        ],
        "specialties": [
          {
            "id": "5b1f0c1e-0000-4000-8000-000000000002",
            "nombre": "Implantología"
          }
        ]
      }
    ],
    "unmapped_specialties": [
      {
        "id": "5b1f0c1e-0000-4000-8000-000000000004",
        "nombre": "Prostodoncia"
      }
    ],
    "ads": [
      {
        "external_id": "120250735360390233",
        "campaign_name": "Campaña de mensajes IMPLANTES",
        "ad_set_name": "INTERESES",
        "ad_name": "Implantes 2",
        "linea_id": "5b1f0c1e-0000-4000-8000-000000000001",
        "via": "rule",
        "matched": "implant",
        "conversations": 119,
        "leads": 38
      }
    ]
  }
}

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

POST /clinic/lineas

Add a Línea

Find-or-create by name (accent- and case-insensitive). Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

Cuerpo

CampoTipoObligatorioRestricciones
nombrestringsímín. 1, máx. 80
curl -X POST https://api.vitrinadev.com/api/v1/clinic/lineas \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "nombre": "Implantes"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "5b1f0c1e-0000-4000-8000-000000000001",
    "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
    "nombre": "Implantes",
    "is_default": false,
    "archived_at": null,
    "created_at": "2026-10-09T12:00:00.000Z",
    "updated_at": "2026-10-09T12:00:00.000Z"
  }
}

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

PATCH /clinic/lineas/{id}

Rename or archive a Línea

An archived Línea leaves the pickers but keeps the leads that carry it. The default Línea («General») cannot be archived. Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
nombrestring—mín. 1, máx. 80
archivedboolean—
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/lineas/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "nombre": "Implantología"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "5b1f0c1e-0000-4000-8000-000000000001",
    "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
    "nombre": "Implantes",
    "is_default": false,
    "archived_at": null,
    "created_at": "2026-10-09T12:00:00.000Z",
    "updated_at": "2026-10-09T12:00:00.000Z"
  }
}

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

POST /clinic/lineas/{id}/keywords

Add a keyword for leads without an ad

A lead that did not come from an ad gets its Línea from its own words before it falls back to General: the lead's title, the booked cita's tratamiento, the score reason, the conversation summary and the notes are matched (accent- and case-insensitive, from the start of a word) against each Línea's keywords. The first of those fields that names a Línea decides; within it the Línea with the most keywords wins. An ad's Línea and a person's choice are never replaced. No model call. A keyword already pointing at another Línea answers 409. Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
keywordstringsímín. 2, máx. 80
curl -X POST https://api.vitrinadev.com/api/v1/clinic/lineas/<id>/keywords \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keyword": "botox"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "5b1f0c1e-0000-4000-8000-000000000005",
    "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
    "linea_id": "5b1f0c1e-0000-4000-8000-000000000001",
    "keyword": "botox",
    "source": "owner",
    "created_at": "2026-10-10T12:00:00.000Z"
  }
}

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

POST /clinic/lineas/{id}/merge

Merge a Línea into another

Moves every lead, specialty and rule of this Línea to into_id, then removes it. Irreversible. Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
into_iduuidsí
curl -X POST https://api.vitrinadev.com/api/v1/clinic/lineas/<id>/merge \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "into_id": "5b1f0c1e-0000-4000-8000-000000000001"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "into": {
      "id": "5b1f0c1e-0000-4000-8000-000000000001",
      "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
      "nombre": "Implantes",
      "is_default": false,
      "archived_at": null,
      "created_at": "2026-10-09T12:00:00.000Z",
      "updated_at": "2026-10-09T12:00:00.000Z"
    },
    "moved": {
      "leads": 4,
      "specialties": 1,
      "rules": 2
    }
  }
}

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

POST /clinic/lineas/{id}/rules

Map ads to a Línea by a name fragment

An ad whose campaign, ad set or ad name contains the fragment belongs to this Línea; the longest fragment wins. A fragment already pointing at another Línea answers 409. Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
matchstringsímín. 2, máx. 80
curl -X POST https://api.vitrinadev.com/api/v1/clinic/lineas/<id>/rules \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "match": "GABRIELA RIQUELME"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "5b1f0c1e-0000-4000-8000-000000000003",
    "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
    "linea_id": "5b1f0c1e-0000-4000-8000-000000000001",
    "match": "GABRIELA RIQUELME",
    "source": "owner",
    "created_at": "2026-10-09T12:00:00.000Z"
  }
}

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

DELETE /clinic/lineas/keywords/{id}

Remove a keyword

Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/lineas/keywords/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

DELETE /clinic/lineas/rules/{id}

Remove a name fragment rule

Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/lineas/rules/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

PUT /clinic/lineas/specialties/{id}

Place a roster specialty in a Línea

id is a clinic_specialty id. linea_id: null takes it out of its Línea. Requires clinic_admin:write.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
linea_idstring | nullsí
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/lineas/specialties/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "linea_id": "5b1f0c1e-0000-4000-8000-000000000001"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "5b1f0c1e-0000-4000-8000-000000000002",
    "linea_id": "5b1f0c1e-0000-4000-8000-000000000001"
  }
}

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

En esta página