VitrinaAPI

Clinic lines of care

The clinic's short list of what patients come for (Implants, Aesthetics, Endodontics…): every roster specialty and every ad belongs to one line, and every lead carries one.

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.

List services and their prices explains this resource in prose, with runnable examples.

MethodPathWhat it does
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"

Example response (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
      }
    ]
  }
}

Answers: 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.

Body

FieldTypeRequiredConstraints
nombrestringyesmí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"
  }'

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
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"
  }'

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
keywordstringyesmí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"
  }'

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
into_iduuidyes
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"
  }'

Example response (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
    }
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
matchstringyesmí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"
  }'

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/lineas/keywords/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/lineas/rules/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
linea_idstring | nullyes
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"
  }'

Example response (200)

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

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

On this page