VitrinaAPI

Projects

A real-estate developer's projects, their unit models, their stock of units (apartments, houses, parking spaces and storage units) and their versioned price list in UF and PDF quotes with down payment and mortgage payment, with an Excel import.

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.

MethodPathWhat it does
DELETE/case-assignments/{id}Cancel an assignment
POST/case-assignments/{id}/linkA fresh link for an assignment, optionally re-sent
GET/channel-commissionsList channel commissions
POST/channel-commissions/{id}/cancelCancel a commission
POST/channel-commissions/{id}/payMark a commission paid
POST/channel-commissions/milestonesRecord that a lead’s negocio reached a milestone
GET/channel-partnersList corredores externos and referidores
POST/channel-partnersAdd a corredor externo or referidor
GET/channel-partners/{id}One corredor externo or referidor
PATCH/channel-partners/{id}Edit a corredor externo or referidor
GET/channel-partners/{id}/claimsThe buyers a partner brought
POST/channel-partners/{id}/claimsCredit a lead to a corredor or referidor
GET/cobranzaOverdue pie cuotas
GET/contratistasList contratistas
POST/contratistasAdd a contratista
DELETE/contratistas/{id}Remove a contratista
GET/contratistas/{id}One contratista
PATCH/contratistas/{id}Edit a contratista
GET/cotizacion-discount-approvalsThe approver’s queue of discount requests
POST/cotizacion-discount-approvals/{id}/decisionApprove or refuse a discount request
GET/cotizacionesList cotizaciones
POST/cotizacionesBuild a cotización draft
DELETE/cotizaciones/{id}Discard a cotización draft
GET/cotizaciones/{id}Get a cotización
PATCH/cotizaciones/{id}Edit a cotización draft
GET/cotizaciones/{id}/discount-approvalWhere a cotización’s discount stands against the caller’s limit
POST/cotizaciones/{id}/discount-approvalAsk a jefe de sala or gerente to allow the discount
GET/cotizaciones/{id}/pdfThe cotización as a PDF
POST/cotizaciones/{id}/reservaReserve a cotización’s unidades
POST/cotizaciones/{id}/sendSend the cotización PDF by WhatsApp or email
GET/cotizaciones/{id}/viewsWho opened the cotización’s tracked link, and when
POST/cotizaciones/calculateCalculate a cotización without saving it
POST/datahunter-importDatahunter import steps 2–3: dry run, then load the history
GET/datahunter-import/batchesDatahunter import: past batches and what is staged
POST/datahunter-import/columnsDatahunter import step 1: read the files’ columns
DELETE/floor-plans/{id}Remove a floor plan
GET/floor-plans/{id}Get a floor plan
PATCH/floor-plans/{id}Edit a floor plan
PUT/floor-plans/{id}/unitsPlace unidades on a floor plan
GET/handoversList actas de entrega
POST/handoversOpen the acta de entrega of a unidad
DELETE/handovers/{id}Cancel an acta that is not signed
GET/handovers/{id}One acta: checklist by section, photos and casos
PATCH/handovers/{id}Write the acta’s general notes
POST/handovers/{id}/attestConfirm the owner’s identity in person
POST/handovers/{id}/casosOpen the post-venta casos of a signed acta
PATCH/handovers/{id}/items/{itemId}Answer a checklist line
POST/handovers/{id}/mediaAttach photos or videos to the walk
GET/handovers/{id}/pdfDownload the signed acta
POST/handovers/{id}/reopenReopen an acta that was sent, to correct it
POST/handovers/{id}/sendSend the acta to the owner to sign
GET/negociosList negocios
GET/negocios/{id}Get a negocio
GET/negocios/{id}/bank-approvalsA negocio’s bank approvals
POST/negocios/{id}/bank-approvalsTrack a bank’s approval
DELETE/negocios/{id}/bank-approvals/{approvalId}Stop tracking a bank
PATCH/negocios/{id}/bank-approvals/{approvalId}Update a bank’s approval
GET/negocios/{id}/buyer-viewWhat the buyer can be told on WhatsApp
GET/negocios/{id}/cuotas/{number}/paymentHow a cuota can be paid
PATCH/negocios/{id}/deadlinesEdit a negocio’s deadlines
POST/negocios/{id}/desistimientoRecord a desistimiento
GET/negocios/{id}/documentsA negocio’s document checklist
GET/negocios/{id}/documents/{documentId}/fileDownload a checklist document
POST/negocios/{id}/documents/{documentId}/fileUpload a document onto a checklist item
POST/negocios/{id}/documents/{documentId}/reviewApprove or reject a checklist document
POST/negocios/{id}/entregaRecord the entrega
POST/negocios/{id}/escrituraRecord the escritura
POST/negocios/{id}/pie-planSplit the pie into cuotas
POST/negocios/{id}/promesaRecord the promesa
GET/negocios/{id}/signaturesA negocio’s signing requests
POST/negocios/{id}/signaturesSend a negocio document to sign
POST/negocios/{id}/signatures/{signatureId}/cancelCancel a signing request
GET/negocios/{id}/signatures/{signatureId}/files/{file}Download a signing file
POST/negocios/{id}/signatures/{signatureId}/resendSend a new signing link
GET/negocios/{id}/statementA negocio’s account statement
GET/negocios/{id}/statement/pdfThe account statement as a PDF
POST/negocios/{id}/statement/sendSend the account statement to the buyer
GET/postventa/reportPost-venta report by proyecto, partida, estado de garantía or etapa
GET/postventa/unit-ownersThe unidades a contact owns
PUT/postventa/unit-ownersRegister the owner of a unidad
DELETE/postventa/unit-owners/{id}End an ownership
GET/price-listsList lista de precios versions
POST/price-listsOpen a draft lista de precios
DELETE/price-lists/{id}Discard a draft lista de precios
GET/price-lists/{id}One lista de precios version, with its prices
PATCH/price-lists/{id}Rename a draft lista de precios
PATCH/price-lists/{id}/entriesSet prices in a draft lista de precios
POST/price-lists/{id}/importPrice import steps 2–3: dry run, then load prices into a draft
POST/price-lists/{id}/publishPublish a lista de precios version
POST/price-lists/import/columnsPrice import step 1: read a file’s columns
GET/project-listingsList portal anuncios (Portal Inmobiliario, Yapo) and their proyecto
POST/project-listingsMap a Portal Inmobiliario anuncio to a proyecto
PATCH/project-listings/{id}Map or unmap a Portal Inmobiliario anuncio
GET/projectsList proyectos
POST/projectsAdd a proyecto
DELETE/projects/{id}Remove a proyecto
GET/projects/{id}One proyecto, with its sala de ventas and tipologías
PATCH/projects/{id}Edit a proyecto
GET/projects/{id}/document-requirementsGet a proyecto’s document checklist
PUT/projects/{id}/document-requirementsSet a proyecto’s document checklist
DELETE/projects/{id}/entrega-checklistBack to the default entrega checklist
GET/projects/{id}/entrega-checklistThe proyecto’s entrega checklist
PUT/projects/{id}/entrega-checklistSet the proyecto’s own entrega checklist
GET/projects/{id}/floor-plansList a proyecto’s floor plans
POST/projects/{id}/floor-plansAdd a floor plan to a proyecto
GET/projects/{id}/marketing-kitRead a proyecto’s marketing kit
PATCH/projects/{id}/marketing-kitEdit a proyecto’s marketing kit
GET/projects/{id}/marketing-kit/audienceWho a kit broadcast would reach
POST/projects/{id}/marketing-kit/broadcastSend a proyecto’s kit message by WhatsApp
POST/projects/{id}/marketing-kit/generateGenerate a proyecto’s marketing kit
GET/projects/{id}/membersList the ejecutivos of a proyecto
POST/projects/{id}/membersAssign an ejecutivo to a proyecto
DELETE/projects/{id}/members/{userId}Unassign an ejecutivo from a proyecto
GET/projects/{id}/milestone-termsGet a proyecto’s milestone deadlines
PUT/projects/{id}/milestone-termsSet a proyecto’s milestone deadlines
GET/projects/{id}/obra-updatesA proyecto’s obra progress posts
POST/projects/{id}/obra-updatesPost an obra progress update
DELETE/projects/{id}/obra-updates/{updateId}Remove an obra post
POST/projects/{id}/obra-updates/{updateId}/announceTell the proyecto’s buyers about an obra post
POST/projects/{id}/obra-updates/{updateId}/photosAdd a photo to an obra post
DELETE/projects/{id}/obra-updates/{updateId}/photos/{index}Remove a photo from an obra post
GET/projects/{id}/obra-updates/{updateId}/photos/{index}Read an obra photo
GET/projects/{id}/pie-termsGet a proyecto’s cuota reminders
PUT/projects/{id}/pie-termsSet a proyecto’s cuota reminders
GET/projects/{id}/publication-readinessCheck a proyecto before publishing it to Portal Inmobiliario
GET/projects/{id}/publicationsList a proyecto’s Portal Inmobiliario anuncios
POST/projects/{id}/publicationsPublish a proyecto and its tipologías to Portal Inmobiliario
DELETE/projects/{id}/publications/{publicationId}Close a proyecto’s listing on Portal Inmobiliario
GET/projects/{id}/reserva-termsGet a proyecto’s reserva terms
PUT/projects/{id}/reserva-termsSet a proyecto’s reserva terms
DELETE/projects/{id}/signing-templates/{kind}Reset a proyecto’s signing template
GET/projects/{id}/signing-templates/{kind}Get a proyecto’s signing template
PUT/projects/{id}/signing-templates/{kind}Set a proyecto’s signing template
GET/projects/{id}/toctocA proyecto’s TocToc publishing payload and state
PUT/projects/{id}/toctocRecord where a proyecto stands on TocToc
GET/projects/{id}/whatsapp-linksGet the tracked WhatsApp links of a proyecto
GET/real-estate-offersList the unit offers sent to old leads
GET/real-estate-offers/settingsRead the unit-offer settings
PUT/real-estate-offers/settingsChange the unit-offer settings
GET/real-estate-reports/projectsDeveloper report per proyecto
GET/reservasList reservas
GET/reservas/{id}Get a reserva
POST/reservas/{id}/cancelRelease a reserva’s hold
GET/reservas/{id}/paymentHow a reserva can be paid
GET/toctoc-connectionThe TocToc lead door of the workspace
POST/toctoc-connection/tokenCreate or rotate the TocToc lead door token
GET/toctoc-listingsList TocToc publications and their proyecto
POST/toctoc-listingsMap a TocToc publication code to a proyecto
PATCH/toctoc-listings/{id}Map or unmap a TocToc publication
GET/unit-modelsList tipologías
POST/unit-modelsAdd a tipología to a proyecto
DELETE/unit-models/{id}Remove a tipología
GET/unit-models/{id}One tipología
PATCH/unit-models/{id}Edit a tipología
GET/unitsList unidades (a proyecto’s stock)
POST/unitsAdd a unidad by hand
DELETE/units/{id}Remove a unidad
GET/units/{id}One unidad
PATCH/units/{id}Edit a unidad
GET/units/{id}/price-historyA unidad’s price in every published lista de precios
POST/units/{id}/release-fieldsLet a unidad’s protected fields follow the import again
POST/units/importImport steps 2–3: dry run, then load a proyecto’s unidades
POST/units/import/columnsImport step 1: read a stock file’s columns

DELETE /case-assignments/{id}

Cancel an assignment

The link stops working at once (it answers 404), the observaciones go back to the caso unassigned, and the contratista is told. A finished assignment cannot be cancelled (409). Requires tickets:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a21",
    "ticket_id": "0199e3a0-0000-7000-8000-000000000a01",
    "contratista_id": "0199e3a0-0000-7000-8000-000000000a11",
    "status": "cancelled",
    "brief": "Revisar la filtración detrás del WC.",
    "owner_window": "Lunes a viernes de 9 a 18",
    "appointment_id": null,
    "visit_starts_at": null,
    "notified_at": "2026-10-20T12:01:00.000Z",
    "notify_error": null,
    "accepted_at": null,
    "scheduled_at": null,
    "done_at": null,
    "declined_at": null,
    "decline_reason": null,
    "completion_notes": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "contratista_name": "Gasfitería Araucanía",
    "contratista_trade": "gasfitería",
    "contratista_whatsapp": "+56912345678",
    "observation_ids": [
      "0199e3a0-0000-7000-8000-000000000b01"
    ]
  }
}

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

POST /case-assignments/{id}/link

A fresh link for an assignment, optionally re-sent

Mints a new 30-day link for the assignment (the old one keeps working until it expires or the assignment is cancelled). With resend: true it is also sent to the contratista by WhatsApp. 409 when the assignment was cancelled. Requires tickets:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/case-assignments/<id>/link \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "resend": true
  }'

Example response (200)

{
  "data": {
    "link": "https://app.example.com/caso/eyJhbGciOi…",
    "notified": true,
    "notify_error": null
  }
}

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

GET /channel-commissions

List channel commissions

Commissions and rewards owed to corredores and referidores, from pending to paid. status=due is the payout queue. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
partner_idqueryuuidno
lead_idqueryuuidno
statusquerypending \due \paid \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/channel-commissions \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000703",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "partner_id": "0199e3a0-0000-7000-8000-000000000701",
      "claim_id": "0199e3a0-0000-7000-8000-000000000702",
      "lead_id": "0199e3a0-0000-7000-8000-000000000704",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "basis": "percent",
      "rate_value": 2,
      "currency": null,
      "milestone": "escritura",
      "status": "pending",
      "base_amount": null,
      "amount": null,
      "due_at": null,
      "paid_at": null,
      "paid_reference": null,
      "cancelled_reason": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  ]
}

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

POST /channel-commissions/{id}/cancel

Cancel a commission

For a desistimiento or an agreement that no longer holds. A paid commission cannot be cancelled (409 CONFLICT, details.code COMMISSION_FINAL). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/channel-commissions/<id>/cancel \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Desistimiento"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000703",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "partner_id": "0199e3a0-0000-7000-8000-000000000701",
    "claim_id": "0199e3a0-0000-7000-8000-000000000702",
    "lead_id": "0199e3a0-0000-7000-8000-000000000704",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "basis": "percent",
    "rate_value": 2,
    "currency": null,
    "milestone": "escritura",
    "status": "cancelled",
    "base_amount": null,
    "amount": null,
    "due_at": null,
    "paid_at": null,
    "paid_reference": null,
    "cancelled_reason": "Desistimiento",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

POST /channel-commissions/{id}/pay

Mark a commission paid

Closes the payout of a due commission with the transfer reference. 409 CONFLICT (details.code COMMISSION_NOT_DUE) when the milestone was not reached or it is already paid; 400 when it has no amount yet. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/channel-commissions/<id>/pay \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "TRF-20261215-0042"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000703",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "partner_id": "0199e3a0-0000-7000-8000-000000000701",
    "claim_id": "0199e3a0-0000-7000-8000-000000000702",
    "lead_id": "0199e3a0-0000-7000-8000-000000000704",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "basis": "percent",
    "rate_value": 2,
    "currency": "UF",
    "milestone": "escritura",
    "status": "paid",
    "base_amount": null,
    "amount": 70,
    "due_at": null,
    "paid_at": "2026-12-15T12:00:00.000Z",
    "paid_reference": "TRF-20261215-0042",
    "cancelled_reason": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

POST /channel-commissions/milestones

Record that a lead’s negocio reached a milestone

Every open commission of the lead whose agreed milestone is this one (or an earlier one) falls due. A percentage is computed on base_amount (default: the lead's value); due with no value yet, it takes its amount when the milestone is recorded again with one. Idempotent. The negocio flow records its milestones itself; this route is the manual door. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

curl -X POST https://api.vitrinadev.com/api/v1/channel-commissions/milestones \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lead_id": "0199e3a0-0000-7000-8000-000000000704",
    "milestone": "escritura",
    "base_amount": 3500,
    "base_currency": "UF"
  }'

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000703",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "partner_id": "0199e3a0-0000-7000-8000-000000000701",
      "claim_id": "0199e3a0-0000-7000-8000-000000000702",
      "lead_id": "0199e3a0-0000-7000-8000-000000000704",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "basis": "percent",
      "rate_value": 2,
      "currency": "UF",
      "milestone": "escritura",
      "status": "due",
      "base_amount": 3500,
      "amount": 70,
      "due_at": "2026-12-01T12:00:00.000Z",
      "paid_at": null,
      "paid_reference": null,
      "cancelled_reason": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  ]
}

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

GET /channel-partners

List corredores externos and referidores

The inmobiliaria's channel: brokers outside the company and referrers, each with the personal code of their link and the commission agreed. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
kindquerybroker \referrerno
statusqueryactive \pausedno
qquerystringnomáx. 100
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/channel-partners \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000701",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "kind": "broker",
      "name": "María López",
      "company": "López Propiedades",
      "email": "[email protected]",
      "phone": "+56912345678",
      "code": "marialopez4k2x",
      "referral_param": "ref=marialopez4k2x",
      "status": "active",
      "protection_days": 90,
      "commission_basis": "percent",
      "commission_value": 2,
      "commission_currency": null,
      "commission_milestone": "escritura",
      "notes": null,
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "active_claim_count": 3,
      "due_commission_count": 1
    }
  ]
}

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

POST /channel-partners

Add a corredor externo or referidor

Creates the partner and their personal code. Append ref=&lt;code&gt; to a proyecto page or form URL: a buyer who submits the proyecto form through it is credited to the partner and protected for protection_days (a second partner claiming the same buyer on the same proyecto inside the window is refused). The commission falls due when the negocio reaches commission_milestone. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

curl -X POST https://api.vitrinadev.com/api/v1/channel-partners \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "broker",
    "name": "María López",
    "company": "López Propiedades",
    "protection_days": 90,
    "commission_basis": "percent",
    "commission_value": 2,
    "commission_milestone": "escritura"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000701",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "kind": "broker",
    "name": "María López",
    "company": "López Propiedades",
    "email": "[email protected]",
    "phone": "+56912345678",
    "code": "marialopez4k2x",
    "referral_param": "ref=marialopez4k2x",
    "status": "active",
    "protection_days": 90,
    "commission_basis": "percent",
    "commission_value": 2,
    "commission_currency": null,
    "commission_milestone": "escritura",
    "notes": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

GET /channel-partners/{id}

One corredor externo or referidor

Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/channel-partners/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000701",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "kind": "broker",
    "name": "María López",
    "company": "López Propiedades",
    "email": "[email protected]",
    "phone": "+56912345678",
    "code": "marialopez4k2x",
    "referral_param": "ref=marialopez4k2x",
    "status": "active",
    "protection_days": 90,
    "commission_basis": "percent",
    "commission_value": 2,
    "commission_currency": null,
    "commission_milestone": "escritura",
    "notes": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

PATCH /channel-partners/{id}

Edit a corredor externo or referidor

Changes their data, protection window or commission terms, or pauses them (status). New terms apply to buyers credited from now on: the commission of a buyer already credited keeps the terms it was promised under. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/channel-partners/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "protection_days": 120
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000701",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "kind": "broker",
    "name": "María López",
    "company": "López Propiedades",
    "email": "[email protected]",
    "phone": "+56912345678",
    "code": "marialopez4k2x",
    "referral_param": "ref=marialopez4k2x",
    "status": "active",
    "protection_days": 90,
    "commission_basis": "percent",
    "commission_value": 2,
    "commission_currency": null,
    "commission_milestone": "escritura",
    "notes": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

GET /channel-partners/{id}/claims

The buyers a partner brought

Their claims, newest first, including the refused ones (a claim lost to an earlier one still inside its window). Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
statusqueryactive \refusedno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/channel-partners/<id>/claims \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000702",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "partner_id": "0199e3a0-0000-7000-8000-000000000701",
      "contact_id": "0199e3a0-0000-7000-8000-000000000705",
      "lead_id": "0199e3a0-0000-7000-8000-000000000704",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "status": "active",
      "claimed_at": "2026-10-20T12:00:00.000Z",
      "protected_until": "2027-01-18T12:00:00.000Z",
      "refused_by_claim_id": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    }
  ]
}

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

POST /channel-partners/{id}/claims

Credit a lead to a corredor or referidor

Registers by hand that the lead's buyer was brought by the partner (a buyer who reached WhatsApp instead of the form). The buyer is protected for the partner's window. The same partner claiming again inside their window changes nothing (200). A different partner claiming the same buyer on the same proyecto inside someone's window is refused with 409 CONFLICT whose details.code is LEAD_PROTECTED and carries protected_until and protected_by_partner_id. A paused partner cannot claim (400). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos: the channel is managed by whoever sees every proyecto.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/channel-partners/<id>/claims \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lead_id": "0199e3a0-0000-7000-8000-000000000704"
  }'

Example response (200)

{
  "data": {
    "outcome": "already_credited",
    "claim": {
      "id": "0199e3a0-0000-7000-8000-000000000702",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "partner_id": "0199e3a0-0000-7000-8000-000000000701",
      "contact_id": "0199e3a0-0000-7000-8000-000000000705",
      "lead_id": "0199e3a0-0000-7000-8000-000000000704",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "status": "active",
      "claimed_at": "2026-10-20T12:00:00.000Z",
      "protected_until": "2027-01-18T12:00:00.000Z",
      "refused_by_claim_id": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "commission": {
      "id": "0199e3a0-0000-7000-8000-000000000703",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "partner_id": "0199e3a0-0000-7000-8000-000000000701",
      "claim_id": "0199e3a0-0000-7000-8000-000000000702",
      "lead_id": "0199e3a0-0000-7000-8000-000000000704",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "basis": "percent",
      "rate_value": 2,
      "currency": null,
      "milestone": "escritura",
      "status": "pending",
      "base_amount": null,
      "amount": null,
      "due_at": null,
      "paid_at": null,
      "paid_reference": null,
      "cancelled_reason": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  }
}

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

GET /cobranza

Overdue pie cuotas

Every cuota past its due day with money still owed, oldest first, with the negocio, buyer, proyecto, ejecutivo and days overdue. Filter by project_id. Requires payments:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/cobranza \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "display_id": "NG-3",
      "buyer_name": "Camila Rojas",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "days_overdue": 4,
      "cuota": {
        "number": 1,
        "due_on": "2026-11-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "overdue",
        "is_overdue": true
      }
    }
  ],
  "meta": {
    "pagination": {
      "limit": 100,
      "offset": 0
    }
  }
}

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

GET /contratistas

List contratistas

The external companies and technicians that repair post-venta casos. Filters: q (name or trade), trade, active. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
qquerystringnomáx. 100
tradequerystringnomáx. 120
activequerybooleanno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/contratistas \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000a11",
      "name": "Gasfitería Araucanía",
      "trade": "gasfitería",
      "whatsapp": "+56912345678",
      "email": null,
      "zones": [
        "Ñuñoa",
        "Providencia"
      ],
      "notes": null,
      "active": true,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /contratistas

Add a contratista

Names are unique among the workspace’s live contratistas (409 otherwise). whatsapp is stored as E.164; it is where the work and the link are sent. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/contratistas \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gasfitería Araucanía",
    "trade": "gasfitería",
    "whatsapp": "+56912345678",
    "zones": [
      "Ñuñoa",
      "Providencia"
    ]
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a11",
    "name": "Gasfitería Araucanía",
    "trade": "gasfitería",
    "whatsapp": "+56912345678",
    "email": null,
    "zones": [
      "Ñuñoa",
      "Providencia"
    ],
    "notes": null,
    "active": true,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

DELETE /contratistas/{id}

Remove a contratista

Soft delete. Assignments already sent keep working until they finish. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

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

GET /contratistas/{id}

One contratista

Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/contratistas/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a11",
    "name": "Gasfitería Araucanía",
    "trade": "gasfitería",
    "whatsapp": "+56912345678",
    "email": null,
    "zones": [
      "Ñuñoa",
      "Providencia"
    ],
    "notes": null,
    "active": true,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

PATCH /contratistas/{id}

Edit a contratista

Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/contratistas/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "active": false
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a11",
    "name": "Gasfitería Araucanía",
    "trade": "gasfitería",
    "whatsapp": "+56912345678",
    "email": null,
    "zones": [
      "Ñuñoa",
      "Providencia"
    ],
    "notes": null,
    "active": true,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

GET /cotizacion-discount-approvals

The approver’s queue of discount requests

Requests with the cotización, buyer and proyecto they are about, newest first. Default status=pending. Narrowed to the caller’s proyectos (a jefe de sala sees their sala’s; a gerente all). Requires cotizacion_discount:approve. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
statusquerypending \approved \refused \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/cotizacion-discount-approvals \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "status": "pending",
      "requested_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "requested_at": "2026-10-20T12:00:00.000Z",
      "note": "Cliente cierra hoy si llegamos al 6 %.",
      "requested_percent": 6,
      "limit_percent": 3,
      "currency": "UF",
      "list_total": 3510,
      "discount_total": 210.6,
      "total": 3299.4,
      "decided_by": null,
      "decided_at": null,
      "decision_reason": null,
      "cotizacion_display_id": "CZ-12",
      "cotizacion_status": "draft",
      "buyer_name": "Camila Rojas",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /cotizacion-discount-approvals/{id}/decision

Approve or refuse a discount request

approve makes the cotización sendable for any discount up to the one asked; refuse needs a reason, which returns to the ejecutivo with the cotización. Terminal: a decided request cannot be decided again (409 approval_not_pending). Nobody decides their own request (409 self_decision). The ejecutivo is told in the bell and both outcomes are audited. 404 when the cotización is outside the caller’s proyectos. Requires cotizacion_discount:approve alone. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
decisionapprove \refuseyes
reasonstring—mín. 1, máx. 1000
curl -X POST https://api.vitrinadev.com/api/v1/cotizacion-discount-approvals/<id>/decision \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "approve"
  }'

Example response (200)

{
  "data": {
    "approval": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "status": "approved",
      "requested_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "requested_at": "2026-10-20T12:00:00.000Z",
      "note": "Cliente cierra hoy si llegamos al 6 %.",
      "requested_percent": 6,
      "limit_percent": 3,
      "currency": "UF",
      "list_total": 3510,
      "discount_total": 210.6,
      "total": 3299.4,
      "decided_by": "0199e3a0-0000-7000-8000-0000000000cc",
      "decided_at": "2026-10-20T12:10:00.000Z",
      "decision_reason": null
    },
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "cotizacion_display_id": "CZ-12"
  }
}

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

GET /cotizaciones

List cotizaciones

Newest first, without lines or financing (GET /cotizaciones/\{id\}). Filters: lead_id, project_id, status. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
lead_idqueryuuidno
project_idqueryuuid[]no
statusquerydraft \sent \accepted \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/cotizaciones \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000901",
      "display_id": "CZ-12",
      "status": "sent",
      "buyer_name": "Camila Rojas",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": null,
      "conversation_id": null,
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
      "price_list_version_number": 2,
      "price_list_superseded": false,
      "currency": "UF",
      "uf_value_clp": 39532.12,
      "uf_value_date": "2026-10-20",
      "valid_until": "2026-10-27",
      "discount_type": "percent",
      "discount_value": 0,
      "list_total": 3510,
      "line_discount_total": 0,
      "global_discount_total": 0,
      "discount_total": 0,
      "total": 3510,
      "discount_percent": 0,
      "notes": null,
      "template_version": "cotizacion/2026-10",
      "presentation": {
        "ejecutivo": {
          "name": "Valentina Soto",
          "phone": "+56987654321",
          "email": "[email protected]"
        },
        "sala": {
          "name": "Sala Los Aromos",
          "address": "Av. Vicuña Mackenna 9000, La Florida",
          "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
        },
        "image_url": null
      },
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:05:00.000Z",
      "sent_at": "2026-10-20T12:05:00.000Z",
      "accepted_at": null,
      "rejected_at": null,
      "voided_at": null,
      "voided_reason": null,
      "is_expired": false
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    },
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

POST /cotizaciones

Build a cotización draft

Builds a draft from unidades: a departamento or casa plus estacionamientos and bodegas, all of ONE proyecto and disponible. Prices come from the proyecto’s current published lista de precios (or price_list_version_id), which the cotización PINS: a newer list never rewrites it. Currency UF (default) or CLP; per-line and global discounts; validity (valid_until or valid_days, default 7); and the financing block (pie %, pie in cuotas, crédito hipotecario → dividendo and renta requerida; omitted → defaults, null → none). Only a name is needed for the buyer, never a RUT. 404 for a unidad or lead outside the caller’s proyectos; 409 unit_not_available, project_has_no_price_list; 400 unit_not_priced. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

Body

FieldTypeRequiredConstraints
lead_idstring | null—
buyer_namestring | null—mín. 1, máx. 200
linesobject[]yes
currencyUF \CLP—
valid_untilstring | null—patrón ^\d{4}-\d{2}-\d{2}$
valid_daysinteger | null—≥ 1, ≤ 365
discountobject | null—
financingobject | null—
notesstring | null—máx. 2000
price_list_version_idstring | null—
curl -X POST https://api.vitrinadev.com/api/v1/cotizaciones \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "lines": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401"
      },
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000402"
      },
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000403"
      }
    ],
    "currency": "UF",
    "valid_days": 7,
    "discount": {
      "type": "percent",
      "value": 2
    },
    "financing": {
      "pie_percent": 20,
      "pie_installments": 36,
      "credit_years": 30,
      "credit_rate_annual": 4.5
    }
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000901",
    "display_id": "CZ-12",
    "status": "draft",
    "buyer_name": "Camila Rojas",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": null,
    "conversation_id": null,
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
    "price_list_version_number": 2,
    "price_list_superseded": false,
    "currency": "UF",
    "uf_value_clp": 39532.12,
    "uf_value_date": "2026-10-20",
    "valid_until": "2026-10-27",
    "discount_type": "percent",
    "discount_value": 0,
    "list_total": 3510,
    "line_discount_total": 0,
    "global_discount_total": 0,
    "discount_total": 0,
    "total": 3510,
    "discount_percent": 0,
    "notes": null,
    "template_version": "cotizacion/2026-10",
    "presentation": {
      "ejecutivo": {
        "name": "Valentina Soto",
        "phone": "+56987654321",
        "email": "[email protected]"
      },
      "sala": {
        "name": "Sala Los Aromos",
        "address": "Av. Vicuña Mackenna 9000, La Florida",
        "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
      },
      "image_url": null
    },
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:05:00.000Z",
    "sent_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "voided_at": null,
    "voided_reason": null,
    "is_expired": false,
    "total_uf": 3510,
    "total_clp": 138760000,
    "lines": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 0,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000401",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "offering_number": "1203",
        "detail": {
          "model_name": "3D2B 78 m²",
          "bedrooms": 3,
          "bathrooms": 2,
          "usable_area_m2": 72.5,
          "terrace_area_m2": 6,
          "floor": 12,
          "orientation": "norte"
        },
        "quantity": 1,
        "unit_price": 3310,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 3310,
        "discount_total": 0,
        "total": 3310
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 1,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000402",
        "kind": "estacionamiento",
        "label": "Estacionamiento 45",
        "offering_number": "45",
        "detail": {},
        "quantity": 1,
        "unit_price": 200,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 200,
        "discount_total": 0,
        "total": 200
      }
    ],
    "financing": {
      "pie_percent": 20,
      "pie_amount": 702,
      "pie_installments": 36,
      "pie_installment_amount": 19.5,
      "credit_amount": 2808,
      "credit_years": 30,
      "credit_rate_annual": 4.5,
      "monthly_payment": 14.23,
      "monthly_payment_clp": 562600,
      "income_ratio": 0.25,
      "required_income_clp": 2250400
    },
    "sends": []
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

DELETE /cotizaciones/{id}

Discard a cotización draft

Drafts only: a sent cotización is a record and cannot be deleted (409 cotizacion_not_draft). Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000901",
    "deleted": true
  }
}

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

GET /cotizaciones/{id}

Get a cotización

The offer with its lines, financing block and every send. price_list_superseded tells a draft it must be re-priced. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/cotizaciones/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000901",
    "display_id": "CZ-12",
    "status": "sent",
    "buyer_name": "Camila Rojas",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": null,
    "conversation_id": null,
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
    "price_list_version_number": 2,
    "price_list_superseded": false,
    "currency": "UF",
    "uf_value_clp": 39532.12,
    "uf_value_date": "2026-10-20",
    "valid_until": "2026-10-27",
    "discount_type": "percent",
    "discount_value": 0,
    "list_total": 3510,
    "line_discount_total": 0,
    "global_discount_total": 0,
    "discount_total": 0,
    "total": 3510,
    "discount_percent": 0,
    "notes": null,
    "template_version": "cotizacion/2026-10",
    "presentation": {
      "ejecutivo": {
        "name": "Valentina Soto",
        "phone": "+56987654321",
        "email": "[email protected]"
      },
      "sala": {
        "name": "Sala Los Aromos",
        "address": "Av. Vicuña Mackenna 9000, La Florida",
        "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
      },
      "image_url": null
    },
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:05:00.000Z",
    "sent_at": "2026-10-20T12:05:00.000Z",
    "accepted_at": null,
    "rejected_at": null,
    "voided_at": null,
    "voided_reason": null,
    "is_expired": false,
    "total_uf": 3510,
    "total_clp": 138760000,
    "lines": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 0,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000401",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "offering_number": "1203",
        "detail": {
          "model_name": "3D2B 78 m²",
          "bedrooms": 3,
          "bathrooms": 2,
          "usable_area_m2": 72.5,
          "terrace_area_m2": 6,
          "floor": 12,
          "orientation": "norte"
        },
        "quantity": 1,
        "unit_price": 3310,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 3310,
        "discount_total": 0,
        "total": 3310
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 1,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000402",
        "kind": "estacionamiento",
        "label": "Estacionamiento 45",
        "offering_number": "45",
        "detail": {},
        "quantity": 1,
        "unit_price": 200,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 200,
        "discount_total": 0,
        "total": 200
      }
    ],
    "financing": {
      "pie_percent": 20,
      "pie_amount": 702,
      "pie_installments": 36,
      "pie_installment_amount": 19.5,
      "credit_amount": 2808,
      "credit_years": 30,
      "credit_rate_annual": 4.5,
      "monthly_payment": 14.23,
      "monthly_payment_clp": 562600,
      "income_ratio": 0.25,
      "required_income_clp": 2250400
    },
    "sends": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "channel": "whatsapp",
        "recipient_hint": "+56•••••4567",
        "sent_by": "0199e3a0-0000-7000-8000-0000000000bb",
        "sent_by_kind": "ui_member",
        "sent_at": "2026-10-20T12:05:00.000Z"
      }
    ]
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

PATCH /cotizaciones/{id}

Edit a cotización draft

Drafts only (409 cotizacion_not_draft once sent). Any field of the create body; the figures are recomputed. A draft keeps the lista de precios version it pinned unless refresh_prices: true re-prices it on the proyecto’s current list. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
lead_idstring | null—
buyer_namestring | null—mín. 1, máx. 200
linesobject[]—
currencyUF \CLP—
valid_untilstring | null—patrón ^\d{4}-\d{2}-\d{2}$
valid_daysinteger | null—≥ 1, ≤ 365
discountobject | null—
financingobject | null—
notesstring | null—máx. 2000
price_list_version_idstring | null—
refresh_pricesboolean—
curl -X PATCH https://api.vitrinadev.com/api/v1/cotizaciones/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "discount": {
      "type": "percent",
      "value": 3
    },
    "refresh_prices": true
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000901",
    "display_id": "CZ-12",
    "status": "draft",
    "buyer_name": "Camila Rojas",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": null,
    "conversation_id": null,
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
    "price_list_version_number": 2,
    "price_list_superseded": false,
    "currency": "UF",
    "uf_value_clp": 39532.12,
    "uf_value_date": "2026-10-20",
    "valid_until": "2026-10-27",
    "discount_type": "percent",
    "discount_value": 0,
    "list_total": 3510,
    "line_discount_total": 0,
    "global_discount_total": 0,
    "discount_total": 0,
    "total": 3510,
    "discount_percent": 0,
    "notes": null,
    "template_version": "cotizacion/2026-10",
    "presentation": {
      "ejecutivo": {
        "name": "Valentina Soto",
        "phone": "+56987654321",
        "email": "[email protected]"
      },
      "sala": {
        "name": "Sala Los Aromos",
        "address": "Av. Vicuña Mackenna 9000, La Florida",
        "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
      },
      "image_url": null
    },
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:05:00.000Z",
    "sent_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "voided_at": null,
    "voided_reason": null,
    "is_expired": false,
    "total_uf": 3510,
    "total_clp": 138760000,
    "lines": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 0,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000401",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "offering_number": "1203",
        "detail": {
          "model_name": "3D2B 78 m²",
          "bedrooms": 3,
          "bathrooms": 2,
          "usable_area_m2": 72.5,
          "terrace_area_m2": 6,
          "floor": 12,
          "orientation": "norte"
        },
        "quantity": 1,
        "unit_price": 3310,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 3310,
        "discount_total": 0,
        "total": 3310
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 1,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000402",
        "kind": "estacionamiento",
        "label": "Estacionamiento 45",
        "offering_number": "45",
        "detail": {},
        "quantity": 1,
        "unit_price": 200,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 200,
        "discount_total": 0,
        "total": 200
      }
    ],
    "financing": {
      "pie_percent": 20,
      "pie_amount": 702,
      "pie_installments": 36,
      "pie_installment_amount": 19.5,
      "credit_amount": 2808,
      "credit_years": 30,
      "credit_rate_annual": 4.5,
      "monthly_payment": 14.23,
      "monthly_payment_clp": 562600,
      "income_ratio": 0.25,
      "required_income_clp": 2250400
    },
    "sends": []
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

GET /cotizaciones/{id}/discount-approval

Where a cotización’s discount stands against the caller’s limit

The caller’s role limit (limit_percent, null = none), the cotización’s effective discount_percent, whether it exceeds_limit, the highest discount an approval has allowed (approved_percent), whether the caller can_send it as it stands, the open request (pending) and every request (approvals, newest first, with who decided and why). A refusal returns the cotización to the ejecutivo with its decision_reason. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/cotizaciones/<id>/discount-approval \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "limit_percent": 3,
    "discount_percent": 6,
    "exceeds_limit": true,
    "approved_percent": null,
    "can_send": false,
    "pending": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "status": "pending",
      "requested_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "requested_at": "2026-10-20T12:00:00.000Z",
      "note": "Cliente cierra hoy si llegamos al 6 %.",
      "requested_percent": 6,
      "limit_percent": 3,
      "currency": "UF",
      "list_total": 3510,
      "discount_total": 210.6,
      "total": 3299.4,
      "decided_by": null,
      "decided_at": null,
      "decision_reason": null
    },
    "approvals": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000a01",
        "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
        "status": "pending",
        "requested_by": "0199e3a0-0000-7000-8000-0000000000bb",
        "requested_at": "2026-10-20T12:00:00.000Z",
        "note": "Cliente cierra hoy si llegamos al 6 %.",
        "requested_percent": 6,
        "limit_percent": 3,
        "currency": "UF",
        "list_total": 3510,
        "discount_total": 210.6,
        "total": 3299.4,
        "decided_by": null,
        "decided_at": null,
        "decision_reason": null
      }
    ]
  }
}

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

POST /cotizaciones/{id}/discount-approval

Ask a jefe de sala or gerente to allow the discount

A draft whose discount is above the caller’s role limit cannot be sent (409 discount_approval_required on POST /cotizaciones/\{id\}/send) until a request is approved. This opens the request and tells, in the bell, every member holding cotizacion_discount:approve whose proyectos include this one. One request is open per cotización: asking again returns it (200, created: false) without telling anyone twice. Editing the draft’s discount supersedes the open request. 409 discount_within_limit when no approval is needed; 409 cotizacion_not_draft once sent. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
notestring—mín. 1, máx. 1000
curl -X POST https://api.vitrinadev.com/api/v1/cotizaciones/<id>/discount-approval \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "note": "Cliente cierra hoy si llegamos al 6 %."
  }'

Example response (200)

{
  "data": {
    "approval": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "status": "pending",
      "requested_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "requested_at": "2026-10-20T12:00:00.000Z",
      "note": "Cliente cierra hoy si llegamos al 6 %.",
      "requested_percent": 6,
      "limit_percent": 3,
      "currency": "UF",
      "list_total": 3510,
      "discount_total": 210.6,
      "total": 3299.4,
      "decided_by": null,
      "decided_at": null,
      "decision_reason": null
    },
    "created": false,
    "notified": 0
  }
}

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

GET /cotizaciones/{id}/pdf

The cotización as a PDF

The branded PDF (the workspace’s logo and colour): lines, discounts, totals in UF and CLP, validity, and the financing block (pie, cuotas, crédito, dividendo, renta requerida). A sent cotización re-prints from its frozen figures; a draft prints marked «VISTA PREVIA». Cache-Control: private, no-store. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/cotizaciones/<id>/pdf \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /cotizaciones/{id}/reserva

Reserve a cotización’s unidades

From a SENT, still-valid cotización: every unidad on it becomes bloqueada until the deadline (the proyecto’s hold_hours, or hold_hours), and the buyer owes the reserva (amount + currency, else the proyecto’s reserva terms; a UF amount is charged in CLP at today’s UF). The payment is offered by the workspace’s charging preference for a reserva: a payment link (the connected provider, expiring with the hold) and/or the bank transfer, which the transfer matcher settles from the buyer’s comprobante. With deliver (default) the message goes to the buyer’s conversation as the teammate (inside the channel window; otherwise delivery.status: not_sent and the text to copy). When the reserva is fully paid the unidades become reservada, the cotización accepted and a negocio is opened (GET /negocios); the ejecutivo is notified. Unpaid at the deadline, the unidades return to disponible. 409: unit_not_available (another buyer holds one: nothing is blocked), reserva_pending, cotizacion_not_reservable, cotizacion_expired, no_contact, uf_unavailable; 400 reserva_amount_required. Requires leads:write (+ messages:send and conversations:write to deliver). Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
amountnumber—> 0, ≤ 1000000000
currencyUF \CLP—
hold_hoursinteger—≥ 1, ≤ 720
co_buyer_contact_idsuuid[]—
conversation_iduuid—
deliverboolean—
curl -X POST https://api.vitrinadev.com/api/v1/cotizaciones/<id>/reserva \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 20,
    "currency": "UF"
  }'

Example response (201)

{
  "data": {
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "pending",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": null,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": null,
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null,
      "paid_clp": 0,
      "outstanding_clp": 790642,
      "payment_state": "pending",
      "is_overdue": false
    },
    "payment": {
      "mode": "mp_first",
      "link": {
        "checkout_id": "0199e3a0-0000-7000-8000-000000000d01",
        "checkout_url": "https://api.vitrinadev.com/p/0199e3a0-0000-7000-8000-000000000d01",
        "amount_clp": 790642
      },
      "transfer_accounts": [
        {
          "alias": "Cuenta corriente",
          "bank": "Banco de Chile",
          "account_type": "corriente",
          "account_number": "00123456789",
          "holder_name": "Inmobiliaria Los Aromos SpA",
          "holder_rut": "76.123.456-7"
        }
      ]
    },
    "text": "Hola Camila, dejamos bloqueada Departamento 1203 · 3D2B 78 m² y Estacionamiento E-12 de Parque Los Aromos para ti hasta el 22/10 a las 12:00.\nPara confirmar la reserva, paga $790.642 antes de esa hora.\n\nPaga aquí: https://api.vitrinadev.com/p/…",
    "delivery": {
      "status": "sent",
      "reason": null,
      "message_id": "0199e3a0-0000-7000-8000-000000000f01",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "channel": "whatsapp"
    }
  }
}

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

POST /cotizaciones/{id}/send

Send the cotización PDF by WhatsApp or email

Renders the PDF and delivers it: whatsapp on the buyer’s conversation (only inside Meta’s 24 h window: 409 whatsapp_window_closed, 409 no_whatsapp_thread) or email to the lead contact’s address (409 no_email). A draft is re-priced with today’s UF, delivered, and only then frozen as sent; a failed delivery leaves it a draft. A draft on a superseded lista de precios is a 409 price_list_superseded (re-price it first); an expired or voided one is a 409. A draft whose discount is above the sender’s role limit (max_discount_percent) is a 409 discount_approval_required until a jefe de sala or gerente approves it (POST /cotizaciones/\{id\}/discount-approval). A sent cotización can be re-sent as it was. Records who sent it and a masked recipient hint. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
channelwhatsapp \emailyes
messagestring—mín. 1, máx. 1000
curl -X POST https://api.vitrinadev.com/api/v1/cotizaciones/<id>/send \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp"
  }'

Example response (200)

{
  "data": {
    "cotizacion": {
      "id": "0199e3a0-0000-7000-8000-000000000901",
      "display_id": "CZ-12",
      "status": "sent",
      "buyer_name": "Camila Rojas",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": null,
      "conversation_id": null,
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
      "price_list_version_number": 2,
      "price_list_superseded": false,
      "currency": "UF",
      "uf_value_clp": 39532.12,
      "uf_value_date": "2026-10-20",
      "valid_until": "2026-10-27",
      "discount_type": "percent",
      "discount_value": 0,
      "list_total": 3510,
      "line_discount_total": 0,
      "global_discount_total": 0,
      "discount_total": 0,
      "total": 3510,
      "discount_percent": 0,
      "notes": null,
      "template_version": "cotizacion/2026-10",
      "presentation": {
        "ejecutivo": {
          "name": "Valentina Soto",
          "phone": "+56987654321",
          "email": "[email protected]"
        },
        "sala": {
          "name": "Sala Los Aromos",
          "address": "Av. Vicuña Mackenna 9000, La Florida",
          "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
        },
        "image_url": null
      },
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:05:00.000Z",
      "sent_at": "2026-10-20T12:05:00.000Z",
      "accepted_at": null,
      "rejected_at": null,
      "voided_at": null,
      "voided_reason": null,
      "is_expired": false,
      "total_uf": 3510,
      "total_clp": 138760000,
      "lines": [
        {
          "id": "0199e3a0-0000-7000-8000-000000000902",
          "position": 0,
          "offering_type": "property",
          "offering_id": "0199e3a0-0000-7000-8000-000000000401",
          "kind": "departamento",
          "label": "Departamento 1203 · 3D2B 78 m²",
          "offering_number": "1203",
          "detail": {
            "model_name": "3D2B 78 m²",
            "bedrooms": 3,
            "bathrooms": 2,
            "usable_area_m2": 72.5,
            "terrace_area_m2": 6,
            "floor": 12,
            "orientation": "norte"
          },
          "quantity": 1,
          "unit_price": 3310,
          "discount_type": "percent",
          "discount_value": 0,
          "list_total": 3310,
          "discount_total": 0,
          "total": 3310
        },
        {
          "id": "0199e3a0-0000-7000-8000-000000000902",
          "position": 1,
          "offering_type": "property",
          "offering_id": "0199e3a0-0000-7000-8000-000000000402",
          "kind": "estacionamiento",
          "label": "Estacionamiento 45",
          "offering_number": "45",
          "detail": {},
          "quantity": 1,
          "unit_price": 200,
          "discount_type": "percent",
          "discount_value": 0,
          "list_total": 200,
          "discount_total": 0,
          "total": 200
        }
      ],
      "financing": {
        "pie_percent": 20,
        "pie_amount": 702,
        "pie_installments": 36,
        "pie_installment_amount": 19.5,
        "credit_amount": 2808,
        "credit_years": 30,
        "credit_rate_annual": 4.5,
        "monthly_payment": 14.23,
        "monthly_payment_clp": 562600,
        "income_ratio": 0.25,
        "required_income_clp": 2250400
      },
      "sends": [
        {
          "id": "0199e3a0-0000-7000-8000-000000000902",
          "channel": "whatsapp",
          "recipient_hint": "+56•••••4567",
          "sent_by": "0199e3a0-0000-7000-8000-0000000000bb",
          "sent_by_kind": "ui_member",
          "sent_at": "2026-10-20T12:05:00.000Z"
        }
      ]
    },
    "channel": "whatsapp",
    "recipient_hint": "+56•••••4567",
    "sent_at": "2026-10-20T12:05:00.000Z"
  }
}

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

GET /cotizaciones/{id}/views

Who opened the cotización’s tracked link, and when

Every sent cotización carries a tracked link (it is in the message the buyer receives). Each person who opens the page or downloads its PDF through it leaves an anonymous event: a stable visitor number (1 is the first person; more than one means the link was passed on, shared: true), a device class and the time. No ip, no user agent, no identity. The ejecutivo is alerted in the bell on the first open, on a second visitor and on a return after a day. A draft has no events. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/cotizaciones/<id>/views \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "opens": 3,
    "downloads": 1,
    "visitors": 2,
    "shared": true,
    "first_opened_at": "2026-10-20T15:02:00.000Z",
    "last_opened_at": "2026-10-21T09:40:00.000Z",
    "events": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b01",
        "kind": "open",
        "visitor_number": 1,
        "is_first": true,
        "is_new_visitor": true,
        "device": "mobile",
        "occurred_at": "2026-10-20T15:02:00.000Z"
      }
    ]
  }
}

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

POST /cotizaciones/calculate

Calculate a cotización without saving it

Takes the same body as POST /cotizaciones and answers with the figures it would have (list total, discounts, total in UF and CLP, pie, dividendo, renta requerida) so a form can show them live. Saves nothing. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches the cotizaciones of their proyectos and leads; anything else is a 404.

Body

FieldTypeRequiredConstraints
lead_idstring | null—
buyer_namestring | null—mín. 1, máx. 200
linesobject[]yes
currencyUF \CLP—
valid_untilstring | null—patrón ^\d{4}-\d{2}-\d{2}$
valid_daysinteger | null—≥ 1, ≤ 365
discountobject | null—
financingobject | null—
notesstring | null—máx. 2000
price_list_version_idstring | null—
curl -X POST https://api.vitrinadev.com/api/v1/cotizaciones/calculate \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "lines": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401"
      },
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000402"
      },
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000403"
      }
    ],
    "currency": "UF",
    "valid_days": 7,
    "discount": {
      "type": "percent",
      "value": 2
    },
    "financing": {
      "pie_percent": 20,
      "pie_installments": 36,
      "credit_years": 30,
      "credit_rate_annual": 4.5
    }
  }'

Example response (200)

{
  "data": {
    "id": null,
    "display_id": "",
    "status": "draft",
    "buyer_name": "Camila Rojas",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": null,
    "conversation_id": null,
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
    "price_list_version_number": 2,
    "price_list_superseded": false,
    "currency": "UF",
    "uf_value_clp": 39532.12,
    "uf_value_date": "2026-10-20",
    "valid_until": "2026-10-27",
    "discount_type": "percent",
    "discount_value": 0,
    "list_total": 3510,
    "line_discount_total": 0,
    "global_discount_total": 0,
    "discount_total": 0,
    "total": 3510,
    "discount_percent": 0,
    "notes": null,
    "template_version": "cotizacion/2026-10",
    "presentation": {
      "ejecutivo": {
        "name": "Valentina Soto",
        "phone": "+56987654321",
        "email": "[email protected]"
      },
      "sala": {
        "name": "Sala Los Aromos",
        "address": "Av. Vicuña Mackenna 9000, La Florida",
        "hours": "Lunes a viernes 10:00–19:00 h · Sábados 10:00–14:00 h"
      },
      "image_url": null
    },
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:05:00.000Z",
    "sent_at": null,
    "accepted_at": null,
    "rejected_at": null,
    "voided_at": null,
    "voided_reason": null,
    "is_expired": false,
    "total_uf": 3510,
    "total_clp": 138760000,
    "lines": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 0,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000401",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "offering_number": "1203",
        "detail": {
          "model_name": "3D2B 78 m²",
          "bedrooms": 3,
          "bathrooms": 2,
          "usable_area_m2": 72.5,
          "terrace_area_m2": 6,
          "floor": 12,
          "orientation": "norte"
        },
        "quantity": 1,
        "unit_price": 3310,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 3310,
        "discount_total": 0,
        "total": 3310
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000902",
        "position": 1,
        "offering_type": "property",
        "offering_id": "0199e3a0-0000-7000-8000-000000000402",
        "kind": "estacionamiento",
        "label": "Estacionamiento 45",
        "offering_number": "45",
        "detail": {},
        "quantity": 1,
        "unit_price": 200,
        "discount_type": "percent",
        "discount_value": 0,
        "list_total": 200,
        "discount_total": 0,
        "total": 200
      }
    ],
    "financing": {
      "pie_percent": 20,
      "pie_amount": 702,
      "pie_installments": 36,
      "pie_installment_amount": 19.5,
      "credit_amount": 2808,
      "credit_years": 30,
      "credit_rate_annual": 4.5,
      "monthly_payment": 14.23,
      "monthly_payment_clp": 562600,
      "income_ratio": 0.25,
      "required_income_clp": 2250400
    },
    "sends": []
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

POST /datahunter-import

Datahunter import steps 2–3: dry run, then load the history

Loads Datahunter's history as records (decision D18): proyectos (matched by name), unidades with their tipologías (the stock import's rules: proyecto + kind + number, protected fields kept), leads, propietarios, cotizaciones and negocios. Send the files as multipart parts (proyectos, unidades, leads, propietarios, cotizaciones, negocios; any subset) with mapping as JSON text, or JSON rows per file (the MCP and extractor path). dry_run defaults to true: all the files are planned together and the report says what would be created, updated, left unchanged, skipped (repeats) or rejected, row by row for the problems, and balances against the source row counts (totals.balanced). People (leads, owners, buyers) are matched to existing contacts by RUT, then phone, then email, and the same person on several rows is one contact; a matched contact is never rewritten. Imported leads are history: contacts are import-only (no conversation, no automation reaches them), leads keep their original dates and carry source import, and nothing is sent. Lead etapas map to the pipeline’s stages through stage_mapping (the dry run lists them in lead_stages). Cotizaciones and negocios are staged (counted in staged) until the cotización and negocio tables exist; their contact, proyecto and unidad are already resolved. A negocio or owner raises its unidad’s status (reservada, promesada, vendida) and never lowers it. Send dry_run: false with step to run one file at a time (the screen’s progress), in the order proyectos, unidades, leads, propietarios, cotizaciones, negocios; each step is its own batch and re-running a step changes nothing. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Needs a caller who sees every proyecto (a narrowed ejecutivo answers 403).

curl -X POST https://api.vitrinadev.com/api/v1/datahunter-import \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": {
      "leads": [
        {
          "name": "Ana Soto",
          "rut": "12.345.678-5",
          "phone": "+56987654321",
          "project": "Parque Aromos",
          "stage": "Cotizó",
          "created_at": "03/05/2024"
        }
      ]
    },
    "dry_run": true
  }'

Example response (200)

{
  "data": {
    "dry_run": true,
    "step": null,
    "batch_ids": [],
    "mapping": {
      "leads": {
        "name": "Nombre",
        "project": "Proyecto"
      }
    },
    "ignored_status_columns": [],
    "pipeline": {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "name": "Ventas"
    },
    "entities": {
      "leads": {
        "summary": {
          "source_rows": 1840,
          "create": 1790,
          "update": 0,
          "unchanged": 0,
          "skipped": 12,
          "error": 38,
          "balanced": true
        },
        "issues": [],
        "issues_truncated": false,
        "sample": []
      }
    },
    "totals": {
      "source_rows": 1840,
      "create": 1790,
      "update": 0,
      "unchanged": 0,
      "skipped": 12,
      "error": 38,
      "balanced": true
    },
    "contacts": {
      "people": 1604,
      "create": 1590,
      "match_existing": 14,
      "merged_in_file": 186
    },
    "unit_models_to_create": 0,
    "unit_status_changes": {
      "reservada": 0,
      "promesada": 0,
      "vendida": 0,
      "unchanged": 0
    },
    "lead_stages": [],
    "negocios_by_stage": {
      "reserva": 0,
      "promesa": 0,
      "escritura": 0,
      "entrega": 0,
      "desistido": 0
    },
    "staged": {
      "cotizacion": 0,
      "negocio": 0
    },
    "written": null
  }
}

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

GET /datahunter-import/batches

Datahunter import: past batches and what is staged

The committed steps, newest first, each with its reconciliation counts, plus how many carried-over records wait in each state (staged cotizaciones and negocios wait for their tables). Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Needs a caller who sees every proyecto (a narrowed ejecutivo answers 403).

curl https://api.vitrinadev.com/api/v1/datahunter-import/batches \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "batches": [],
    "staged": [
      {
        "entity": "negocio",
        "state": "staged",
        "n": 212
      }
    ]
  }
}

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

POST /datahunter-import/columns

Datahunter import step 1: read the files’ columns

Multipart, one part per file, named proyectos, unidades, leads, propietarios, cotizaciones or negocios (.xlsx, .xls or .csv, up to 20 MB each, any subset). For each file returns its header, the first rows, the mapping we suggest from the column names and every field a column can feed — the mapping screen. A file whose columns are named by the field keys (what the #4806 extractor writes) maps with no input. Writes and stores nothing. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Needs a caller who sees every proyecto (a narrowed ejecutivo answers 403).

curl -X POST https://api.vitrinadev.com/api/v1/datahunter-import/columns \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -F "leads=(binary: leads-datahunter.xlsx)"

Example response (200)

{
  "data": {
    "files": [
      {
        "entity": "leads",
        "format": "xlsx",
        "sheet_name": "Leads",
        "header": [
          "Nombre",
          "RUT",
          "Celular",
          "Proyecto",
          "Etapa",
          "Fecha"
        ],
        "row_count": 1840,
        "sample": [
          [
            "Ana Soto",
            "12.345.678-5",
            "+56 9 8765 4321",
            "Parque Aromos",
            "Cotizó",
            "03/05/2024"
          ]
        ],
        "suggested_mapping": {
          "name": "Nombre",
          "rut": "RUT",
          "phone": "Celular",
          "project": "Proyecto",
          "stage": "Etapa",
          "created_at": "Fecha"
        },
        "ignored_status_columns": [],
        "fields": [
          {
            "key": "name",
            "label": "Nombre",
            "required": true,
            "description": "The person’s full name."
          }
        ]
      }
    ]
  }
}

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

DELETE /floor-plans/{id}

Remove a floor plan

Removes the plan and its placements. The unidades themselves are untouched. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "deleted": true
  }
}

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

GET /floor-plans/{id}

Get a floor plan

One plan with the live unidades placed on it. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/floor-plans/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Piso 12",
    "floor": 12,
    "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
    "image_width": 1600,
    "image_height": 1000,
    "sort_order": 12,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "units": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "shape": [
          [
            0.1,
            0.2
          ],
          [
            0.3,
            0.2
          ],
          [
            0.3,
            0.45
          ],
          [
            0.1,
            0.45
          ]
        ]
      }
    ]
  }
}

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

PATCH /floor-plans/{id}

Edit a floor plan

Rename it, change its floor, order or image. Changing the image keeps the placements: their coordinates are fractions of the image, so a re-export of the same plan at another size still fits. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/floor-plans/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Piso 12 (torre A)"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Piso 12",
    "floor": 12,
    "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
    "image_width": 1600,
    "image_height": 1000,
    "sort_order": 12,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "units": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "shape": [
          [
            0.1,
            0.2
          ],
          [
            0.3,
            0.2
          ],
          [
            0.3,
            0.45
          ],
          [
            0.1,
            0.45
          ]
        ]
      }
    ]
  }
}

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

PUT /floor-plans/{id}/units

Place unidades on a floor plan

Replaces every placement of the plan with units: each a unidad of the plan’s own proyecto, once, with a polygon of 3 to 64 [x, y] points as fractions (0..1) of the image, origin top-left. A unidad of another proyecto, a removed one or a repeated one is a 400 naming the ids, and nothing is written. An empty list clears the plan. A unidad may sit on several plans (a dúplex). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/floor-plans/<id>/units \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "units": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "shape": [
          [
            0.1,
            0.2
          ],
          [
            0.3,
            0.2
          ],
          [
            0.3,
            0.45
          ],
          [
            0.1,
            0.45
          ]
        ]
      }
    ]
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Piso 12",
    "floor": 12,
    "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
    "image_width": 1600,
    "image_height": 1000,
    "sort_order": 12,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "units": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "shape": [
          [
            0.1,
            0.2
          ],
          [
            0.3,
            0.2
          ],
          [
            0.3,
            0.45
          ],
          [
            0.1,
            0.45
          ]
        ]
      }
    ]
  }
}

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

GET /handovers

List actas de entrega

Newest first, with the proyecto, the unidad, the owner and how many lines are observed / still unreviewed. Filters: project_id, property_id, status. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
project_idqueryuuidno
property_idqueryuuidno
statusquerydraft \pending_signature \signed \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/handovers \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "project_name": "Vista Golf Departamentos",
      "unit_label": "departamento 302",
      "contact_name": "Marcela Ortiz",
      "items_total": 30,
      "items_observations": 1,
      "items_pending": 3
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /handovers

Open the acta de entrega of a unidad

One live acta per unidad (409 handover_id of the existing one). The checklist is born with it: the proyecto’s own list (/projects/\{id\}/entrega-checklist) or the platform default, in walking order. The owner is the unidad’s registered owner or the contact_id given (and becomes the registered owner). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/handovers \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "property_id": "0199e3a0-0000-7000-8000-000000000401",
    "contact_id": "0199e3a0-0000-7000-8000-000000000c01"
  }'

Example response (201)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

DELETE /handovers/{id}

Cancel an acta that is not signed

A signed acta is never cancelled (409). A cancelled one frees the unidad for a new acta. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

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

GET /handovers/{id}

One acta: checklist by section, photos and casos

Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/handovers/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

PATCH /handovers/{id}

Write the acta’s general notes

Drafts only (409 otherwise). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/handovers/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Se entregan 2 juegos de llaves."
  }'

Example response (200)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

POST /handovers/{id}/attest

Confirm the owner’s identity in person

For the entrega with the owner in front of staff (cédula in hand): the owner signs without a WhatsApp code for the next 12 hours, and the signature is recorded as in_person with who attested. Requires the acta to be sent (pending_signature). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/handovers/<id>/attest \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

POST /handovers/{id}/casos

Open the post-venta casos of a signed acta

Signing opens them already; this is the retry when that step failed. Idempotent: a line that already has its observación is skipped. The lines together open ONE caso with several observaciones; a line marked split_case opens its own. Each caso carries the unidad, the proyecto, the highest priority of its lines and the photos. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/handovers/<id>/casos \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "casos": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000a01",
        "display_id": "T-12",
        "observations": 3
      }
    ]
  }
}

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

PATCH /handovers/{id}/items/{itemId}

Answer a checklist line

state: ok (conforme), observation (a defect), na or pending. An observation needs its partida (preselected from the line) and a note; priority and split_case only mean something on an observation (leaving the state resets them). Drafts only. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
itemIdpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/handovers/<id>/items/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "observation",
    "partida": "instalaciones_sanitarias",
    "note": "El lavamanos gotea por la base."
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b31",
    "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
    "section": "Baños",
    "item_key": "bano_griferia",
    "label": "Grifería, presión de agua y desagües",
    "position": 12,
    "state": "observation",
    "partida": "instalaciones_sanitarias",
    "priority": "normal",
    "note": "El lavamanos gotea por la base.",
    "split_case": false
  }
}

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

POST /handovers/{id}/media

Attach photos or videos to the walk

Upload the file with POST /tenant/media first and send its url, optionally on one line (item_id). A url already on the acta is left as it is. The photos of an observation travel to its caso when the acta is signed. Drafts only. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/handovers/<id>/media \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "item_id": "0199e3a0-0000-7000-8000-000000000b31",
        "url": "https://media.example.com/t/other/lavamanos.jpg",
        "caption": "Gotea por la base"
      }
    ]
  }'

Example response (201)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

GET /handovers/{id}/pdf

Download the signed acta

The sealed PDF, its SHA-256 re-verified on every read: an object that no longer matches what was signed is refused (409). 404 until the acta is signed. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/handovers/<id>/pdf \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /handovers/{id}/reopen

Reopen an acta that was sent, to correct it

Back to draft; the owner’s link stops showing it until it is sent again. Only before it is signed. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/handovers/<id>/reopen \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "handover": {
      "id": "0199e3a0-0000-7000-8000-000000000b21",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
      "status": "draft",
      "notes": null,
      "performed_by": null,
      "performed_at": "2026-10-20T12:00:00.000Z",
      "signer_name": null,
      "signed_at": null,
      "signature_method": null,
      "signed_pdf_sha256": null,
      "signed_pdf_bytes": null,
      "sent_for_signature_at": null,
      "attested_at": null,
      "cancelled_at": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    },
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Vista Golf Departamentos"
    },
    "unit": {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "label": "departamento 302",
      "address": "Av. Vicuña Mackenna 0000",
      "comuna": "La Florida"
    },
    "owner": {
      "id": "0199e3a0-0000-7000-8000-000000000c01",
      "name": "Marcela Ortiz"
    },
    "sections": [
      {
        "title": "Baños",
        "items": [
          {
            "id": "0199e3a0-0000-7000-8000-000000000b31",
            "handover_id": "0199e3a0-0000-7000-8000-000000000b21",
            "section": "Baños",
            "item_key": "bano_griferia",
            "label": "Grifería, presión de agua y desagües",
            "position": 12,
            "state": "observation",
            "partida": "instalaciones_sanitarias",
            "priority": "normal",
            "note": "El lavamanos gotea por la base.",
            "split_case": false
          }
        ]
      }
    ],
    "media": [],
    "casos": [],
    "counts": {
      "total": 30,
      "ok": 24,
      "observation": 1,
      "na": 2,
      "pending": 3
    }
  }
}

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

POST /handovers/{id}/send

Send the acta to the owner to sign

Every line must be answered (400 otherwise, naming how many are pending). The acta is frozen and the owner gets a no-login link (/acta/&lt;token&gt;, 14 days) by WhatsApp text; notified says whether it went out (a closed 24 h window or a missing number comes back in notify_error). With notify: false only the link returns, to show on the staff device at the entrega. Calling it again on a pending acta mints a fresh link. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/handovers/<id>/send \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notify": true
  }'

Example response (200)

{
  "data": {
    "link": "https://app.example.com/acta/eyJhbGciOi…",
    "notified": true,
    "notify_error": null
  }
}

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

GET /negocios

List negocios

An inmobiliaria’s sales from reserva to entrega, newest reserva first. Filters: project_id, stage, lead_id, contact_id. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
stagequeryreservado \promesado \escriturado \
lead_idqueryuuidno
contact_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/negocios \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000b01",
      "display_id": "NG-3",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "buyer_name": "Camila Rojas",
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "stage": "reservado",
      "currency": "UF",
      "total": 3510,
      "uf_value_clp": 39532.12,
      "reserved_at": "2026-10-21T13:10:00.000Z",
      "created_at": "2026-10-21T13:10:00.000Z",
      "updated_at": "2026-10-21T13:10:00.000Z",
      "promesa_due_on": "2026-11-20",
      "promesa_signed_on": null,
      "escritura_due_on": null,
      "escritura_signed_on": null,
      "escritura_notary": null,
      "entrega_due_on": null,
      "entregado_on": null,
      "desisted_on": null,
      "desist_reason": null,
      "desist_money_outcome": null,
      "desist_notes": null,
      "milestone_alerted_key": null,
      "next_deadline": {
        "milestone": "promesa",
        "due_on": "2026-11-20",
        "overdue": false
      }
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

GET /negocios/{id}

Get a negocio

One negocio: the buyer(s), the unidades at the accepted cotización’s prices (with their current status), the stage and the reserva that opened it. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "reservado",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": null,
    "escritura_due_on": null,
    "escritura_signed_on": null,
    "escritura_notary": null,
    "entrega_due_on": null,
    "entregado_on": null,
    "desisted_on": null,
    "desist_reason": null,
    "desist_money_outcome": null,
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": {
      "milestone": "promesa",
      "due_on": "2026-11-20",
      "overdue": false
    },
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

GET /negocios/{id}/bank-approvals

A negocio’s bank approvals

One row per bank: en_evaluacion → pre_aprobado → aprobado | rechazado, with amount, rate, term and dates, side by side. Requires leads:read. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/bank-approvals \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000f21",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "bank": "Banco Estado",
      "status": "pre_aprobado",
      "amount": 2800,
      "currency": "UF",
      "rate_annual": 4.39,
      "term_years": 30,
      "decided_on": "2026-10-28",
      "expires_on": "2027-01-28",
      "notes": null,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_at": "2026-10-25T12:00:00.000Z",
      "updated_at": "2026-10-28T12:00:00.000Z"
    },
    {
      "id": "0199e3a0-0000-7000-8000-000000000f22",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "bank": "Banco de Chile",
      "status": "en_evaluacion",
      "amount": null,
      "currency": null,
      "rate_annual": null,
      "term_years": null,
      "decided_on": null,
      "expires_on": null,
      "notes": null,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_at": "2026-10-25T12:00:00.000Z",
      "updated_at": "2026-10-28T12:00:00.000Z"
    }
  ]
}

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

POST /negocios/{id}/bank-approvals

Track a bank’s approval

One row per bank and negocio (409 bank_already_tracked). amount and currency go together. Requires leads:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
bankstringyesmín. 1, máx. 200
statusen_evaluacion \pre_aprobado \aprobado \
amountnumber | null—> 0, ≤ 1000000000
currencyUF \CLP—
rate_annualnumber | null—≥ 0, ≤ 30
term_yearsinteger | null—≥ 1, ≤ 40
decided_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
expires_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
notesstring | null—máx. 2000
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/bank-approvals \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bank": "Banco Estado",
    "status": "en_evaluacion"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000f21",
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "bank": "Banco Estado",
    "status": "pre_aprobado",
    "amount": 2800,
    "currency": "UF",
    "rate_annual": 4.39,
    "term_years": 30,
    "decided_on": "2026-10-28",
    "expires_on": "2027-01-28",
    "notes": null,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_at": "2026-10-25T12:00:00.000Z",
    "updated_at": "2026-10-28T12:00:00.000Z"
  }
}

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

DELETE /negocios/{id}/bank-approvals/{approvalId}

Stop tracking a bank

Requires leads:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
approvalIdpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/negocios/<id>/bank-approvals/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000f21",
    "deleted": true
  }
}

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

PATCH /negocios/{id}/bank-approvals/{approvalId}

Update a bank’s approval

Any field; the status moves freely as the bank answers. Requires leads:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
approvalIdpathuuidyes

Body

FieldTypeRequiredConstraints
bankstring—mín. 1, máx. 200
statusen_evaluacion \pre_aprobado \aprobado \
amountnumber | null—> 0, ≤ 1000000000
currencyUF \CLP—
rate_annualnumber | null—≥ 0, ≤ 30
term_yearsinteger | null—≥ 1, ≤ 40
decided_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
expires_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
notesstring | null—máx. 2000
curl -X PATCH https://api.vitrinadev.com/api/v1/negocios/<id>/bank-approvals/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "aprobado",
    "amount": 2800,
    "currency": "UF",
    "rate_annual": 4.29,
    "term_years": 30,
    "decided_on": "2026-11-05"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000f21",
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "bank": "Banco Estado",
    "status": "aprobado",
    "amount": 2800,
    "currency": "UF",
    "rate_annual": 4.39,
    "term_years": 30,
    "decided_on": "2026-10-28",
    "expires_on": "2027-01-28",
    "notes": null,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_at": "2026-10-25T12:00:00.000Z",
    "updated_at": "2026-10-28T12:00:00.000Z"
  }
}

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

GET /negocios/{id}/buyer-view

What the buyer can be told on WhatsApp

The negocio exactly as the WhatsApp agent reads it for its buyer (my_property): stage, next deadline, entrega date, pending documents, the pie, the proyecto’s latest obra posts and, for unidades with a registered owner, the status of their post-venta casos. The agent reveals it only to the WhatsApp number of the negocio’s titular or cotitular (post-venta: of the unidad’s owner). Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/buyer-view \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "identity": [
      "Camila Rojas"
    ],
    "negocios": [
      {
        "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
        "display_id": "NG-3",
        "project_name": "Parque Los Aromos",
        "stage": "promesado",
        "stage_label": "Promesa firmada",
        "units": [
          "Departamento 604"
        ],
        "next_deadline": {
          "milestone": "escritura",
          "label": "firma de la escritura",
          "due_on": "2026-12-15",
          "overdue": false
        },
        "entrega": {
          "due_on": "2027-03-30",
          "delivered_on": null
        },
        "documents": {
          "pending": [],
          "total_required": 4
        },
        "pie": null
      }
    ],
    "obra": [],
    "postventa": []
  }
}

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

GET /negocios/{id}/cuotas/{number}/payment

How a cuota can be paid

The cuota’s payment link (the connected provider) and/or transfer accounts, by the workspace’s charging preference. 409 cuota_not_open. Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
numberpathintegeryes≥ 1, ≤ 36
curl https://api.vitrinadev.com/api/v1/negocios/<id>/cuotas/<id>/payment \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "number": 1,
    "due_on": "2026-11-20",
    "amount_clp": 2312629,
    "mode": "mp_first",
    "link": {
      "checkout_id": "0199e3a0-0000-7000-8000-000000000d02",
      "checkout_url": "https://api.vitrinadev.com/p/…",
      "amount_clp": 2312629
    },
    "transfer_accounts": []
  }
}

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

PATCH /negocios/{id}/deadlines

Edit a negocio’s deadlines

Moves the promesa, escritura or entrega deadline (null clears it) and the escritura’s notaría. A moved deadline alerts again if it passes. 409 negocio_closed for a desistido or entregado negocio. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
promesa_due_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
escritura_due_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
entrega_due_onstring | null—patrón ^\d{4}-\d{2}-\d{2}$
escritura_notarystring | null—mín. 1, máx. 200
curl -X PATCH https://api.vitrinadev.com/api/v1/negocios/<id>/deadlines \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "promesa_due_on": "2026-11-30"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "reservado",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": null,
    "escritura_due_on": null,
    "escritura_signed_on": null,
    "escritura_notary": null,
    "entrega_due_on": null,
    "entregado_on": null,
    "desisted_on": null,
    "desist_reason": null,
    "desist_money_outcome": null,
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": {
      "milestone": "promesa",
      "due_on": "2026-11-20",
      "overdue": false
    },
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

POST /negocios/{id}/desistimiento

Record a desistimiento

Before the escritura only (reservado or promesado): the stage becomes desistido, the unidades return to disponible and the negocio stops holding them. Records the reason and the outcome for the money paid (returned | retained | applied), stamped on the reserva cobro as its disposition (devuelto | perdido | aplicado). Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
reasonstringyesmín. 1, máx. 500
money_outcomereturned \retained \applied
notesstring | null—máx. 2000
desisted_onstring—patrón ^\d{4}-\d{2}-\d{2}$
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/desistimiento \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "No obtuvo el crédito hipotecario",
    "money_outcome": "returned"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "desistido",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": null,
    "escritura_due_on": null,
    "escritura_signed_on": null,
    "escritura_notary": null,
    "entrega_due_on": null,
    "entregado_on": null,
    "desisted_on": "2026-12-02",
    "desist_reason": "No obtuvo el crédito hipotecario",
    "desist_money_outcome": "returned",
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": null,
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

GET /negocios/{id}/documents

A negocio’s document checklist

Every item with its status (missing → received → approved | rejected), the file (name, type, size; download with GET …/file), where it came from (the buyer in the chat, a staff upload, a signed document) and the reminders sent. Built from the proyecto’s list on first read. Requires leads:read. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/documents \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "items": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000f11",
        "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
        "key": "liquidaciones",
        "label": "Últimas 3 liquidaciones de sueldo",
        "description": null,
        "required": true,
        "position": 1,
        "status": "received",
        "file": {
          "filename": "liquidaciones.pdf",
          "mime": "application/pdf",
          "size": 182331,
          "uploaded_at": "2026-10-22T14:03:00.000Z"
        },
        "source": "chat",
        "message_id": "88213",
        "received_at": "2026-10-22T14:03:00.000Z",
        "reviewed_at": null,
        "reviewed_by": null,
        "review_note": null,
        "reminder_count": 1,
        "last_reminded_at": "2026-10-21T13:00:00.000Z",
        "created_at": "2026-10-21T13:10:00.000Z",
        "updated_at": "2026-10-22T14:03:00.000Z"
      }
    ],
    "summary": {
      "required": 4,
      "received": 1,
      "approved": 0,
      "pending": 3
    }
  }
}

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

GET /negocios/{id}/documents/{documentId}/file

Download a checklist document

The stored bytes, streamed through the API (nosniff, Cache-Control: private, no-store, sandbox CSP); every read is audited. Requires leads:read. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
documentIdpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/documents/<id>/file \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /negocios/{id}/documents/{documentId}/file

Upload a document onto a checklist item

multipart/form-data with one file part (or the JSON file shape), at most 25 MB; images, PDF and office documents. The item becomes received; uploading again replaces it and clears the review. Requires leads:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
documentIdpathuuidyes

Body

FieldTypeRequiredConstraints
filenamestringyesmín. 1, máx. 255
contentstringyesmín. 1, máx. 35000000
content_encodingutf8 \base64—
content_typestring—máx. 200
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/documents/<id>/file \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "liquidaciones.pdf",
    "content": "JVBERi0xLjQK…",
    "content_encoding": "base64"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000f11",
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "key": "liquidaciones",
    "label": "Últimas 3 liquidaciones de sueldo",
    "description": null,
    "required": true,
    "position": 1,
    "status": "received",
    "file": {
      "filename": "liquidaciones.pdf",
      "mime": "application/pdf",
      "size": 182331,
      "uploaded_at": "2026-10-22T14:03:00.000Z"
    },
    "source": "chat",
    "message_id": "88213",
    "received_at": "2026-10-22T14:03:00.000Z",
    "reviewed_at": null,
    "reviewed_by": null,
    "review_note": null,
    "reminder_count": 1,
    "last_reminded_at": "2026-10-21T13:00:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-22T14:03:00.000Z"
  }
}

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

POST /negocios/{id}/documents/{documentId}/review

Approve or reject a checklist document

A received item only (409 document_missing). Rejecting needs a note: the buyer gets it by WhatsApp (inside the 24 h window) with a request to send it again, and reminders resume. Requires leads:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
documentIdpathuuidyes

Body

FieldTypeRequiredConstraints
statusapproved \rejectedyes
notestring | null—máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/documents/<id>/review \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "rejected",
    "note": "La liquidación de agosto no se lee"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000f11",
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "key": "liquidaciones",
    "label": "Últimas 3 liquidaciones de sueldo",
    "description": null,
    "required": true,
    "position": 1,
    "status": "rejected",
    "file": {
      "filename": "liquidaciones.pdf",
      "mime": "application/pdf",
      "size": 182331,
      "uploaded_at": "2026-10-22T14:03:00.000Z"
    },
    "source": "chat",
    "message_id": "88213",
    "received_at": "2026-10-22T14:03:00.000Z",
    "reviewed_at": null,
    "reviewed_by": null,
    "review_note": "La liquidación de agosto no se lee",
    "reminder_count": 1,
    "last_reminded_at": "2026-10-21T13:00:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-22T14:03:00.000Z"
  }
}

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

POST /negocios/{id}/entrega

Record the entrega

An escriturado negocio only: the stage becomes entregado (post-venta starts). Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
delivered_onstring—patrón ^\d{4}-\d{2}-\d{2}$
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/entrega \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "delivered_on": "2027-06-15"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "entregado",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": null,
    "escritura_due_on": null,
    "escritura_signed_on": null,
    "escritura_notary": null,
    "entrega_due_on": null,
    "entregado_on": "2027-06-15",
    "desisted_on": null,
    "desist_reason": null,
    "desist_money_outcome": null,
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": null,
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

POST /negocios/{id}/escritura

Record the escritura

A promesado negocio only: the stage becomes escriturado and the unidades vendida. The escritura is signed at the notary (notary), never e-signed. Sets the entrega deadline (entrega_due_on, else + the proyecto’s entrega_due_days). Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
signed_onstring—patrón ^\d{4}-\d{2}-\d{2}$
notarystring | null—mín. 1, máx. 200
entrega_due_onstring—patrón ^\d{4}-\d{2}-\d{2}$
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/escritura \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signed_on": "2027-04-30",
    "notary": "Notaría Pérez, Santiago"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "escriturado",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": null,
    "escritura_due_on": null,
    "escritura_signed_on": "2027-04-30",
    "escritura_notary": "Notaría Pérez, Santiago",
    "entrega_due_on": null,
    "entregado_on": null,
    "desisted_on": null,
    "desist_reason": null,
    "desist_money_outcome": null,
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": {
      "milestone": "promesa",
      "due_on": "2026-11-20",
      "overdue": false
    },
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

POST /negocios/{id}/pie-plan

Split the pie into cuotas

The pie (default: the accepted cotización’s pie minus the reserva already paid, in the negocio’s currency) in 1–36 monthly cuotas from first_due_on (default a month from today); the last cuota absorbs the rounding. Each cuota is a real_estate_pie obligation of the buyer, paid by its own link (GET …/cuotas/\{number\}/payment) or by transfer (the ledger’s matcher settles the oldest open cuota first). A UF cuota is charged at the day’s UF until money is applied to it. The buyer is reminded before and after each due day (/projects/\{id\}/pie-terms). 409 pie_plan_exists (pass replace while nothing is paid), pie_plan_has_payments, negocio_stage_conflict, uf_unavailable. Requires leads:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
installmentsinteger—≥ 1, ≤ 36
totalnumber—> 0, ≤ 1000000000
first_due_onstring—patrón ^\d{4}-\d{2}-\d{2}$
replaceboolean—
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/pie-plan \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "installments": 12,
    "first_due_on": "2026-11-20"
  }'

Example response (201)

{
  "data": {
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "buyer_name": "Camila Rojas",
    "project_name": "Parque Los Aromos",
    "currency": "UF",
    "total": 3510,
    "reserva": {
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "paid_at": "2026-10-21T13:10:00.000Z"
    },
    "cuotas": [
      {
        "number": 1,
        "due_on": "2026-11-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "pending",
        "is_overdue": false
      },
      {
        "number": 2,
        "due_on": "2026-12-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c22",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "pending",
        "is_overdue": false
      }
    ],
    "totals": {
      "pie_clp": 27751548,
      "paid_clp": 0,
      "outstanding_clp": 27751548,
      "overdue_clp": 0,
      "cuotas_paid": 0,
      "cuotas_overdue": 0
    },
    "next_due": {
      "number": 1,
      "due_on": "2026-11-20",
      "amount": 58.5,
      "currency": "UF",
      "amount_clp": 2312629,
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
      "paid_clp": 0,
      "outstanding_clp": 2312629,
      "state": "pending",
      "is_overdue": false
    },
    "as_of": "2026-10-22"
  }
}

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

POST /negocios/{id}/promesa

Record the promesa

A reservado negocio only (409 negocio_stage_conflict otherwise): the stage becomes promesado, the unidades promesada, and the escritura deadline is set (escritura_due_on, else signed_on + the proyecto’s escritura_due_days). Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
signed_onstring—patrón ^\d{4}-\d{2}-\d{2}$
escritura_due_onstring—patrón ^\d{4}-\d{2}-\d{2}$
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/promesa \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signed_on": "2026-11-10"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "reserva_id": "0199e3a0-0000-7000-8000-000000000a01",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "buyer_name": "Camila Rojas",
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "stage": "promesado",
    "currency": "UF",
    "total": 3510,
    "uf_value_clp": 39532.12,
    "reserved_at": "2026-10-21T13:10:00.000Z",
    "created_at": "2026-10-21T13:10:00.000Z",
    "updated_at": "2026-10-21T13:10:00.000Z",
    "promesa_due_on": "2026-11-20",
    "promesa_signed_on": "2026-11-10",
    "escritura_due_on": "2027-05-09",
    "escritura_signed_on": null,
    "escritura_notary": null,
    "entrega_due_on": null,
    "entregado_on": null,
    "desisted_on": null,
    "desist_reason": null,
    "desist_money_outcome": null,
    "desist_notes": null,
    "milestone_alerted_key": null,
    "next_deadline": {
      "milestone": "escritura",
      "due_on": "2027-05-09",
      "overdue": false
    },
    "buyers": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b11",
        "contact_id": "0199e3a0-0000-7000-8000-000000000302",
        "contact_name": "Camila Rojas",
        "role": "titular",
        "position": 0
      }
    ],
    "units": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000b21",
        "property_id": "0199e3a0-0000-7000-8000-000000000401",
        "property_display_id": "I-41",
        "kind": "departamento",
        "label": "Departamento 1203 · 3D2B 78 m²",
        "unit_number": "1203",
        "price": 3310,
        "position": 0,
        "released_at": null,
        "status": "reservada"
      },
      {
        "id": "0199e3a0-0000-7000-8000-000000000b22",
        "property_id": "0199e3a0-0000-7000-8000-000000000402",
        "property_display_id": "I-87",
        "kind": "estacionamiento",
        "label": "Estacionamiento E-12",
        "unit_number": "E-12",
        "price": 200,
        "position": 1,
        "released_at": null,
        "status": "reservada"
      }
    ],
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "paid",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": "2026-10-21T13:10:00.000Z",
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null
    }
  }
}

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

GET /negocios/{id}/signatures

A negocio’s signing requests

Every request, newest first, with its status, the document hash, which files exist and the evidence trail (requested, code sent/verified, cédula and selfie submitted with their hashes, sent to the provider, signed — each with instant, IP and user agent). Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/signatures \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000c11",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "kind": "orden_reserva",
      "provider": "documenso",
      "status": "pending_identity",
      "title": "Orden de reserva",
      "signer_name": "Camila Rojas",
      "signer_phone_hint": "+56•••••4567",
      "document_sha256": "9f2c…",
      "otp_channel": null,
      "otp_verified_at": null,
      "identity_submitted_at": null,
      "signed_at": null,
      "expires_at": "2026-10-29T15:00:00.000Z",
      "cancelled_at": null,
      "files": {
        "unsigned": true,
        "signed": false,
        "certificate": false,
        "id_front": false,
        "selfie": false
      },
      "evidence": [
        {
          "at": "2026-10-22T15:00:00.000Z",
          "event": "requested"
        }
      ],
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_at": "2026-10-22T15:00:00.000Z"
    }
  ]
}

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

POST /negocios/{id}/signatures

Send a negocio document to sign

Generates the document (the proyecto’s template filled with the negocio, its cotización and reserva) as a branded PDF, freezes its SHA-256 and sends the buyer a WhatsApp link (deliver, default true, as the teammate; link is returned once to copy). On the link the buyer reads the PDF, types an SMS code, uploads a photo of the cédula and a selfie, and signs with the provider (Documenso, FES). The signed PDF and the certificate land on the negocio checklist and the ejecutivo is told. 409 signature_pending, negocio_stage_conflict, signing_not_configured. Requires leads:write (+ messages:send, conversations:write to deliver). Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
kindorden_reservayes
deliverboolean—
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/signatures \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "orden_reserva"
  }'

Example response (201)

{
  "data": {
    "signature": {
      "id": "0199e3a0-0000-7000-8000-000000000c11",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "kind": "orden_reserva",
      "provider": "documenso",
      "status": "pending_identity",
      "title": "Orden de reserva",
      "signer_name": "Camila Rojas",
      "signer_phone_hint": "+56•••••4567",
      "document_sha256": "9f2c…",
      "otp_channel": null,
      "otp_verified_at": null,
      "identity_submitted_at": null,
      "signed_at": null,
      "expires_at": "2026-10-29T15:00:00.000Z",
      "cancelled_at": null,
      "files": {
        "unsigned": true,
        "signed": false,
        "certificate": false,
        "id_front": false,
        "selfie": false
      },
      "evidence": [
        {
          "at": "2026-10-22T15:00:00.000Z",
          "event": "requested"
        }
      ],
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_at": "2026-10-22T15:00:00.000Z"
    },
    "link": "https://app.vitrinadev.com/firmar/q9Xc…",
    "text": "Hola Camila, te enviamos tu orden de reserva para firmar: …",
    "delivery": {
      "status": "sent",
      "reason": null,
      "message_id": "88300",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "channel": "whatsapp"
    }
  }
}

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

POST /negocios/{id}/signatures/{signatureId}/cancel

Cancel a signing request

The link stops working and the provider envelope is cancelled. Requires leads:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
signatureIdpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/signatures/<id>/cancel \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000c11",
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "kind": "orden_reserva",
    "provider": "documenso",
    "status": "cancelled",
    "title": "Orden de reserva",
    "signer_name": "Camila Rojas",
    "signer_phone_hint": "+56•••••4567",
    "document_sha256": "9f2c…",
    "otp_channel": null,
    "otp_verified_at": null,
    "identity_submitted_at": null,
    "signed_at": null,
    "expires_at": "2026-10-29T15:00:00.000Z",
    "cancelled_at": null,
    "files": {
      "unsigned": true,
      "signed": false,
      "certificate": false,
      "id_front": false,
      "selfie": false
    },
    "evidence": [
      {
        "at": "2026-10-22T15:00:00.000Z",
        "event": "requested"
      }
    ],
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_at": "2026-10-22T15:00:00.000Z"
  }
}

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

GET /negocios/{id}/signatures/{signatureId}/files/{file}

Download a signing file

unsigned (what was sent), signed, certificate (the provider’s), id_front or selfie. Streamed, no-store, audited. Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
signatureIdpathuuidyes
filepathunsigned \signed \certificate \
curl https://api.vitrinadev.com/api/v1/negocios/<id>/signatures/<id>/files/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /negocios/{id}/signatures/{signatureId}/resend

Send a new signing link

A live request only (409 signature_not_live); the previous link stops working. Requires leads:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
signatureIdpathuuidyes

Body

FieldTypeRequiredConstraints
deliverboolean—
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/signatures/<id>/resend \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Example response (200)

{
  "data": {
    "signature": {
      "id": "0199e3a0-0000-7000-8000-000000000c11",
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "kind": "orden_reserva",
      "provider": "documenso",
      "status": "pending_identity",
      "title": "Orden de reserva",
      "signer_name": "Camila Rojas",
      "signer_phone_hint": "+56•••••4567",
      "document_sha256": "9f2c…",
      "otp_channel": null,
      "otp_verified_at": null,
      "identity_submitted_at": null,
      "signed_at": null,
      "expires_at": "2026-10-29T15:00:00.000Z",
      "cancelled_at": null,
      "files": {
        "unsigned": true,
        "signed": false,
        "certificate": false,
        "id_front": false,
        "selfie": false
      },
      "evidence": [
        {
          "at": "2026-10-22T15:00:00.000Z",
          "event": "requested"
        }
      ],
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_at": "2026-10-22T15:00:00.000Z"
    },
    "link": "https://app.vitrinadev.com/firmar/Zr3…",
    "text": "…",
    "delivery": {
      "status": "sent",
      "reason": null,
      "message_id": "88301",
      "conversation_id": null,
      "channel": "whatsapp"
    }
  }
}

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

GET /negocios/{id}/statement

A negocio’s account statement

The estado de cuenta: the reserva and every cuota with what is paid, owed and overdue (from the ledger), the totals and the next cuota due. Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/statement \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
    "display_id": "NG-3",
    "buyer_name": "Camila Rojas",
    "project_name": "Parque Los Aromos",
    "currency": "UF",
    "total": 3510,
    "reserva": {
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "paid_at": "2026-10-21T13:10:00.000Z"
    },
    "cuotas": [
      {
        "number": 1,
        "due_on": "2026-11-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "pending",
        "is_overdue": false
      },
      {
        "number": 2,
        "due_on": "2026-12-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c22",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "pending",
        "is_overdue": false
      }
    ],
    "totals": {
      "pie_clp": 27751548,
      "paid_clp": 0,
      "outstanding_clp": 27751548,
      "overdue_clp": 0,
      "cuotas_paid": 0,
      "cuotas_overdue": 0
    },
    "next_due": {
      "number": 1,
      "due_on": "2026-11-20",
      "amount": 58.5,
      "currency": "UF",
      "amount_clp": 2312629,
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
      "paid_clp": 0,
      "outstanding_clp": 2312629,
      "state": "pending",
      "is_overdue": false
    },
    "as_of": "2026-10-22"
  }
}

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

GET /negocios/{id}/statement/pdf

The account statement as a PDF

Branded with the workspace’s paper. Requires leads:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/negocios/<id>/statement/pdf \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /negocios/{id}/statement/send

Send the account statement to the buyer

The PDF on the buyer’s WhatsApp thread, inside Meta’s 24 h window (409 statement_window_closed, statement_no_thread). The buyer can also ask the agent for it. Requires leads:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/negocios/<id>/statement/send \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "sent": true,
    "statement": {
      "negocio_id": "0199e3a0-0000-7000-8000-000000000b01",
      "display_id": "NG-3",
      "buyer_name": "Camila Rojas",
      "project_name": "Parque Los Aromos",
      "currency": "UF",
      "total": 3510,
      "reserva": {
        "amount": 20,
        "currency": "UF",
        "amount_clp": 790642,
        "paid_at": "2026-10-21T13:10:00.000Z"
      },
      "cuotas": [
        {
          "number": 1,
          "due_on": "2026-11-20",
          "amount": 58.5,
          "currency": "UF",
          "amount_clp": 2312629,
          "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
          "paid_clp": 0,
          "outstanding_clp": 2312629,
          "state": "pending",
          "is_overdue": false
        },
        {
          "number": 2,
          "due_on": "2026-12-20",
          "amount": 58.5,
          "currency": "UF",
          "amount_clp": 2312629,
          "obligation_id": "0199e3a0-0000-7000-8000-000000000c22",
          "paid_clp": 0,
          "outstanding_clp": 2312629,
          "state": "pending",
          "is_overdue": false
        }
      ],
      "totals": {
        "pie_clp": 27751548,
        "paid_clp": 0,
        "outstanding_clp": 27751548,
        "overdue_clp": 0,
        "cuotas_paid": 0,
        "cuotas_overdue": 0
      },
      "next_due": {
        "number": 1,
        "due_on": "2026-11-20",
        "amount": 58.5,
        "currency": "UF",
        "amount_clp": 2312629,
        "obligation_id": "0199e3a0-0000-7000-8000-000000000c21",
        "paid_clp": 0,
        "outstanding_clp": 2312629,
        "state": "pending",
        "is_overdue": false
      },
      "as_of": "2026-10-22"
    }
  }
}

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

GET /postventa/report

Post-venta report by proyecto, partida, estado de garantía or etapa

Casos grouped by group_by (project | partida | warranty_status | stage | priority | contratista): casos, observaciones, open and resolved, observaciones outside their garantía, the median days to resolve, the oldest open caso and the owners’ average 1 to 5 rating. Only tickets that carry a proyecto or an observación count. format=csv returns one row per observación (casos without one included) as a UTF-8 CSV with BOM for a spreadsheet. Requires tickets:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
group_byqueryproject \partida \warranty_status \
project_idqueryuuidno
fromquerystringnodate-time
toquerystringnodate-time
formatqueryjson \csvno
curl https://api.vitrinadev.com/api/v1/postventa/report \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "key": "0199e3a0-0000-7000-8000-000000000101",
      "label": "Vista Golf Departamentos",
      "casos": 9,
      "observaciones": 11,
      "abiertos": 4,
      "resueltos": 5,
      "fuera_de_plazo": 1,
      "mediana_dias_resolucion": 7,
      "antiguedad_max_dias": 12.5,
      "encuesta_promedio": 4.4
    }
  ],
  "meta": {
    "group_by": "project"
  }
}

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

GET /postventa/unit-owners

The unidades a contact owns

The owner registry the agent identifies a WhatsApp number against: live ownerships of the contact, with the dates their garantías start from. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
contact_idqueryuuidyes
curl https://api.vitrinadev.com/api/v1/postventa/unit-owners \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "unit_owner_id": "0199e3a0-0000-7000-8000-000000000d01",
      "property_id": "0199e3a0-0000-7000-8000-000000000401",
      "display_id": "I-42",
      "unit_number": "302",
      "kind": "departamento",
      "title": "Departamento 302 · Vista Golf Departamentos",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Vista Golf Departamentos",
      "escritura_at": "2024-04-02",
      "cbr_registered_at": "2024-04-20",
      "delivered_at": "2024-05-10",
      "recepcion_definitiva_at": "2024-03-15"
    }
  ]
}

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

PUT /postventa/unit-owners

Register the owner of a unidad

Idempotent on (unidad, contact): a second call updates the dates it names. cbr_registered_at starts the 3-year terminaciones garantía; the proyecto’s recepcion_definitiva_at starts the 5- and 10-year ones. The entrega of a negocio writes this row when negocios ship. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X PUT https://api.vitrinadev.com/api/v1/postventa/unit-owners \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "property_id": "0199e3a0-0000-7000-8000-000000000401",
    "contact_id": "0199e3a0-0000-7000-8000-000000000c01",
    "escritura_at": "2024-04-02",
    "cbr_registered_at": "2024-04-20",
    "delivered_at": "2024-05-10"
  }'

Example response (201)

{
  "data": {
    "unit_owner_id": "0199e3a0-0000-7000-8000-000000000d01",
    "property_id": "0199e3a0-0000-7000-8000-000000000401",
    "display_id": "I-42",
    "unit_number": "302",
    "kind": "departamento",
    "title": "Departamento 302 · Vista Golf Departamentos",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Vista Golf Departamentos",
    "escritura_at": "2024-04-02",
    "cbr_registered_at": "2024-04-20",
    "delivered_at": "2024-05-10",
    "recepcion_definitiva_at": "2024-03-15"
  }
}

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

DELETE /postventa/unit-owners/{id}

End an ownership

The unidad was sold on, or the row was a mistake. Casos already opened keep their unidad. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

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

GET /price-lists

List lista de precios versions

The versions of the proyectos’ listas de precios, newest first within each proyecto (without their prices: GET /price-lists/\{id\}). Filters: project_id, status (draft | published), current=true (only each proyecto’s current list). A version’s id is the stable price_list_version_id a cotización records. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
statusquerydraft \published[]no
currentquerybooleanno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/price-lists \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project": {
        "id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "Parque Los Aromos"
      },
      "version_number": 2,
      "status": "published",
      "is_current": true,
      "name": "Lista noviembre 2026",
      "notes": null,
      "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
      "entry_count": 200,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:30:00.000Z",
      "published_at": "2026-10-20T12:30:00.000Z",
      "published_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "published_by_kind": "ui_member",
      "superseded_at": null,
      "superseded_by_version_id": null
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /price-lists

Open a draft lista de precios

Opens the proyecto’s next version (v1, v2…) as a draft, by default with the current list’s prices copied in (each keeps who set it and when). One draft per proyecto: a second is a 409 price_list_draft_exists with the draft’s id in details.draft_id. Fill it with POST /price-lists/\{id\}/import or PATCH /price-lists/\{id\}/entries, then publish it. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/price-lists \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Lista noviembre 2026",
    "copy_from": "current"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "version_number": 2,
    "status": "draft",
    "is_current": false,
    "name": "Lista noviembre 2026",
    "notes": null,
    "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "entry_count": 200,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:30:00.000Z",
    "published_at": null,
    "published_by": null,
    "published_by_kind": null,
    "superseded_at": null,
    "superseded_by_version_id": null
  }
}

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

DELETE /price-lists/{id}

Discard a draft lista de precios

Deletes the draft and its prices; its version number is not reused. Drafts only: a published version is frozen (409 price_list_not_draft). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "deleted": true
  }
}

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

GET /price-lists/{id}

One lista de precios version, with its prices

Every unidad price of the version in UF and in CLP with the day’s UF (meta.uf_value), who set each price and when, and the price in the version it is compared with (a draft: the current list; a published version: the one it replaced) for a diff. Old versions stay readable forever. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/price-lists/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "version_number": 2,
    "status": "published",
    "is_current": true,
    "name": "Lista noviembre 2026",
    "notes": null,
    "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "entry_count": 200,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:30:00.000Z",
    "published_at": "2026-10-20T12:30:00.000Z",
    "published_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "published_by_kind": "ui_member",
    "superseded_at": null,
    "superseded_by_version_id": null,
    "compared_with_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "summary": {
      "entries": 200,
      "added": 0,
      "changed": 150,
      "unchanged": 50,
      "units_without_price": 0,
      "removed_units": 0
    },
    "entries": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "display_id": "I-42",
        "kind": "departamento",
        "unit_number": "1203",
        "title": "Departamento 1203 · Parque Los Aromos",
        "floor": 12,
        "unit_model": {
          "id": "0199e3a0-0000-7000-8000-000000000201",
          "name": "3D2B 78 m²"
        },
        "unit_status": "disponible",
        "unit_removed": false,
        "price_uf": 3310,
        "price_clp": 130851317,
        "previous_price_uf": 3250.5,
        "source": "import",
        "set_by": "0199e3a0-0000-7000-8000-0000000000bb",
        "set_by_kind": "ui_member",
        "set_at": "2026-10-20T12:10:00.000Z"
      }
    ]
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

PATCH /price-lists/{id}

Rename a draft lista de precios

Name and notes. Drafts only: a published version is frozen (409 price_list_not_draft). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/price-lists/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Lista diciembre 2026"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "version_number": 2,
    "status": "draft",
    "is_current": false,
    "name": "Lista noviembre 2026",
    "notes": null,
    "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "entry_count": 200,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:30:00.000Z",
    "published_at": null,
    "published_by": null,
    "published_by_kind": null,
    "superseded_at": null,
    "superseded_by_version_id": null
  }
}

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

PATCH /price-lists/{id}/entries

Set prices in a draft lista de precios

Sets (or, with price_uf: null, removes) the price of one or more unidades of the proyecto, all or nothing; each price is stamped with who set it and when. Answers the whole version, with meta.changes. Drafts only: a published version is frozen (409 price_list_not_draft). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/price-lists/<id>/entries \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "price_uf": 3310
      }
    ]
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "version_number": 2,
    "status": "published",
    "is_current": true,
    "name": "Lista noviembre 2026",
    "notes": null,
    "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "entry_count": 200,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:30:00.000Z",
    "published_at": "2026-10-20T12:30:00.000Z",
    "published_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "published_by_kind": "ui_member",
    "superseded_at": null,
    "superseded_by_version_id": null,
    "compared_with_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "summary": {
      "entries": 200,
      "added": 0,
      "changed": 150,
      "unchanged": 50,
      "units_without_price": 0,
      "removed_units": 0
    },
    "entries": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "display_id": "I-42",
        "kind": "departamento",
        "unit_number": "1203",
        "title": "Departamento 1203 · Parque Los Aromos",
        "floor": 12,
        "unit_model": {
          "id": "0199e3a0-0000-7000-8000-000000000201",
          "name": "3D2B 78 m²"
        },
        "unit_status": "disponible",
        "unit_removed": false,
        "price_uf": 3310,
        "price_clp": 130851317,
        "previous_price_uf": 3250.5,
        "source": "import",
        "set_by": "0199e3a0-0000-7000-8000-0000000000bb",
        "set_by_kind": "ui_member",
        "set_at": "2026-10-20T12:10:00.000Z"
      }
    ]
  },
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

POST /price-lists/{id}/import

Price import steps 2–3: dry run, then load prices into a draft

Loads prices from the inmobiliaria’s Excel or CSV (multipart: file, mapping as JSON text, dry_run, default_kind) or from JSON rows (the MCP path). dry_run defaults to true: the report says row by row what would be added, changed or left unchanged (with the draft’s and the current list’s price), and every error with its field; nothing is written. dry_run: false writes every row without an error in ONE transaction. A row is matched to a unidad of the proyecto by kind + number; a unidad the proyecto does not have is an error (load the stock first with POST /units/import). Unidades missing from the file keep the draft’s price (not_in_file). Drafts only: a published version is frozen (409 price_list_not_draft). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/price-lists/<id>/import \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": [
      {
        "Número": "1203",
        "Tipo": "Depto",
        "Precio UF": "3.310"
      },
      {
        "Número": "E-12",
        "Tipo": "Estacionamiento",
        "Precio UF": 260
      }
    ],
    "mapping": {
      "unit_number": "Número",
      "kind": "Tipo",
      "price_uf": "Precio UF"
    },
    "dry_run": true
  }'

Example response (200)

{
  "data": {
    "dry_run": true,
    "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "mapping": {
      "unit_number": "Número",
      "kind": "Tipo",
      "price_uf": "Precio UF"
    },
    "summary": {
      "total_rows": 2,
      "add": 0,
      "change": 1,
      "unchanged": 0,
      "error": 1,
      "not_in_file": 198
    },
    "rows": [
      {
        "row_number": 2,
        "action": "change",
        "unit_number": "1203",
        "kind": "departamento",
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "display_id": "I-42",
        "price_uf": 3310,
        "draft_price_uf": 3250.5,
        "current_price_uf": 3250.5,
        "errors": []
      },
      {
        "row_number": 3,
        "action": "error",
        "unit_number": "E-12",
        "kind": "estacionamiento",
        "unit_id": null,
        "display_id": null,
        "price_uf": 260,
        "draft_price_uf": null,
        "current_price_uf": null,
        "errors": [
          {
            "field": "row",
            "message": "La unidad estacionamiento E-12 no existe en este proyecto. Agrégala primero en Unidades."
          }
        ]
      }
    ]
  }
}

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

POST /price-lists/{id}/publish

Publish a lista de precios version

In one transaction: the proyecto’s current version is superseded (it stays readable), this draft becomes the published, current version, stamped with who published it and when, and every price is written onto its unidad (sale_price_amount in UF). The list is then the only writer of the proyecto’s unidad prices: sale_price_amount leaves a unidad’s protected_fields, a hand edit of the price answers 409 unit_price_owned_by_price_list, and the stock import ignores its price column. Unidades without a price in the version keep theirs (units_without_price). A cotización priced from an older version keeps that version’s prices. An empty draft is a 409 price_list_empty. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/price-lists/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "price_list": {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project": {
        "id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "Parque Los Aromos"
      },
      "version_number": 2,
      "status": "published",
      "is_current": true,
      "name": "Lista noviembre 2026",
      "notes": null,
      "based_on_version_id": "0199e3a0-0000-7000-8000-000000000500",
      "entry_count": 200,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:30:00.000Z",
      "published_at": "2026-10-20T12:30:00.000Z",
      "published_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "published_by_kind": "ui_member",
      "superseded_at": null,
      "superseded_by_version_id": null
    },
    "superseded_version_id": "0199e3a0-0000-7000-8000-000000000500",
    "units_priced": 200,
    "units_changed": 150,
    "skipped_removed": 0,
    "units_without_price": 0
  }
}

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

POST /price-lists/import/columns

Price import step 1: read a file’s columns

Multipart, the file in a part named file (.xlsx, .xls or .csv, up to 20 MB). Returns the header, the first rows and the mapping we suggest for the three fields a price row needs (unit_number, kind, price_uf). Writes nothing: send the same file to POST /price-lists/\{id\}/import. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/price-lists/import/columns \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -F "file=@/path/to/file.png"

Example response (200)

{
  "data": {
    "format": "xlsx",
    "sheet_name": "Lista",
    "header": [
      "N° Unidad",
      "Tipo",
      "Precio UF"
    ],
    "row_count": 200,
    "sample": [
      [
        "1203",
        "Depto",
        "3.310"
      ]
    ],
    "suggested_mapping": {
      "unit_number": "N° Unidad",
      "kind": "Tipo",
      "price_uf": "Precio UF"
    },
    "fields": [
      {
        "key": "price_uf",
        "label": "Precio (UF)",
        "required": true,
        "description": "List price in UF, two decimals."
      }
    ]
  }
}

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

GET /project-listings

List portal anuncios (Portal Inmobiliario, Yapo) and their proyecto

Anuncios (portal listings) of the workspace with how many leads arrived on each. mapped=false is the queue of anuncios waiting for a proyecto. Requires marketplace:read. A question or contact on a Portal Inmobiliario proyecto, or on a Yapo real-estate ad (matched by its Yapo ad id, adid), becomes a lead on the proyecto its anuncio is mapped to (lead.project_id), owned by one of its ejecutivos. An anuncio nobody has mapped yet is recorded on its first inquiry and that lead is flagged for mapping (project_id null, note on the lead). Only on the Proyectos module; callers whose role narrows proyectos are refused (403).

ParameterInTypeRequiredConstraints
mappedquerytrue \falseno
project_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/project-listings \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
      "external_id": "MLC1234567890",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "title": "Proyecto Parque Central, Ñuñoa",
      "permalink": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "provider": "mercadolibre",
      "lead_count": 4,
      "unmapped_lead_count": 0
    }
  ]
}

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

POST /project-listings

Map a Portal Inmobiliario anuncio to a proyecto

Maps the portal's listing id to a proyecto ahead of its first inquiry. Idempotent on the anuncio: posting an already-seen id re-maps it, stamps the proyecto on its waiting leads and routes the unowned ones to the proyecto's ejecutivos. Requires marketplace:write. A question or contact on a Portal Inmobiliario proyecto, or on a Yapo real-estate ad (matched by its Yapo ad id, adid), becomes a lead on the proyecto its anuncio is mapped to (lead.project_id), owned by one of its ejecutivos. An anuncio nobody has mapped yet is recorded on its first inquiry and that lead is flagged for mapping (project_id null, note on the lead). Only on the Proyectos module; callers whose role narrows proyectos are refused (403).

curl -X POST https://api.vitrinadev.com/api/v1/project-listings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "external_id": "MLC1234567890"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
    "external_id": "MLC1234567890",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "title": "Proyecto Parque Central, Ñuñoa",
    "permalink": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "leads_mapped": 0
  }
}

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

PATCH /project-listings/{id}

Map or unmap a Portal Inmobiliario anuncio

Sets the proyecto of an anuncio (project_id), or clears it with null. Mapping stamps the proyecto on the leads waiting on the anuncio and routes the unowned ones to its ejecutivos; unmapping only affects leads that arrive afterwards. Requires marketplace:write. A question or contact on a Portal Inmobiliario proyecto, or on a Yapo real-estate ad (matched by its Yapo ad id, adid), becomes a lead on the proyecto its anuncio is mapped to (lead.project_id), owned by one of its ejecutivos. An anuncio nobody has mapped yet is recorded on its first inquiry and that lead is flagged for mapping (project_id null, note on the lead). Only on the Proyectos module; callers whose role narrows proyectos are refused (403).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/project-listings/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
    "external_id": "MLC1234567890",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "title": "Proyecto Parque Central, Ñuñoa",
    "permalink": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "leads_mapped": 3
  }
}

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

GET /projects

List proyectos

Live proyectos, newest first, every filter ANDed: comuna (accent- and case-insensitive), stage (etapa), subsidy (qualifies for any), sales_office_location_id, free text q. Each row carries unit_model_count and the stock summary units_total, units_available and price_from_uf (the «desde» in UF) and member_count (the team). Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
qquerystringnomáx. 200
comunaquerystring[]no
stagequeryen_blanco \en_verde \entrega_inmediata[]
subsidyqueryDS19 \DS1 \DS49[]
sales_office_location_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/projects \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "name": "Parque Los Aromos",
      "description": "Condominio de departamentos a pasos del metro.",
      "region": "Metropolitana",
      "comuna": "La Florida",
      "address": "Av. Vicuña Mackenna 0000",
      "stage": "en_verde",
      "delivery_date": "2027-12-01",
      "recepcion_definitiva_at": null,
      "on_call_user_ids": [],
      "subsidies": [
        "DS19"
      ],
      "sales_office_location_id": "0199e3a0-0000-7000-8000-000000000301",
      "photos": [
        "https://media.example.com/t/other/aromos-1.jpg"
      ],
      "brochure_url": "https://media.example.com/t/other/aromos-brochure.pdf",
      "meta_ad_ids": [
        "120211111111111111"
      ],
      "whatsapp_code": "RK482",
      "visor_show_prices": false,
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "unit_model_count": 1,
      "units_total": 40,
      "units_available": 12,
      "price_from_uf": 3250.5,
      "member_count": 2
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /projects

Add a proyecto

Names are unique among the workspace’s live proyectos (409 otherwise). The sala de ventas is an active location of the workspace (GET /locations); one sala may serve several proyectos. Media fields (photos, brochure_url) are public URLs: upload the file with POST /tenant/media first and send its url. Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/projects \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Parque Los Aromos",
    "comuna": "La Florida",
    "region": "Metropolitana",
    "stage": "en_verde",
    "delivery_date": "2027-12-01",
    "subsidies": [
      "DS19"
    ],
    "sales_office_location_id": "0199e3a0-0000-7000-8000-000000000301"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000101",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "name": "Parque Los Aromos",
    "description": "Condominio de departamentos a pasos del metro.",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "address": "Av. Vicuña Mackenna 0000",
    "stage": "en_verde",
    "delivery_date": "2027-12-01",
    "recepcion_definitiva_at": null,
    "on_call_user_ids": [],
    "subsidies": [
      "DS19"
    ],
    "sales_office_location_id": "0199e3a0-0000-7000-8000-000000000301",
    "photos": [
      "https://media.example.com/t/other/aromos-1.jpg"
    ],
    "brochure_url": "https://media.example.com/t/other/aromos-brochure.pdf",
    "meta_ad_ids": [
      "120211111111111111"
    ],
    "whatsapp_code": "RK482",
    "visor_show_prices": false,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sales_office": {
      "id": "0199e3a0-0000-7000-8000-000000000301",
      "display_id": "B-2",
      "name": "Sala de ventas La Florida"
    },
    "unit_models": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000201",
        "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
        "project_id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "3D2B 78 m²",
        "description": "Esquina, con terraza en L.",
        "bedrooms": 3,
        "bathrooms": 2,
        "usable_area_m2": 72.5,
        "terrace_area_m2": 6,
        "total_area_m2": 78.5,
        "floor_plans": [
          "https://media.example.com/t/other/planta-3d2b.pdf"
        ],
        "renders": [
          "https://media.example.com/t/other/render-3d2b.jpg"
        ],
        "tour_360_url": "https://tour.example.com/3d2b",
        "created_by": null,
        "created_at": "2026-10-20T12:00:00.000Z",
        "updated_at": "2026-10-20T12:00:00.000Z"
      }
    ]
  }
}

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

DELETE /projects/{id}

Remove a proyecto

Takes the proyecto and its tipologías out of every list. Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Parque Los Aromos",
    "deleted": true
  }
}

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

GET /projects/{id}

One proyecto, with its sala de ventas and tipologías

Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000101",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "name": "Parque Los Aromos",
    "description": "Condominio de departamentos a pasos del metro.",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "address": "Av. Vicuña Mackenna 0000",
    "stage": "en_verde",
    "delivery_date": "2027-12-01",
    "recepcion_definitiva_at": null,
    "on_call_user_ids": [],
    "subsidies": [
      "DS19"
    ],
    "sales_office_location_id": "0199e3a0-0000-7000-8000-000000000301",
    "photos": [
      "https://media.example.com/t/other/aromos-1.jpg"
    ],
    "brochure_url": "https://media.example.com/t/other/aromos-brochure.pdf",
    "meta_ad_ids": [
      "120211111111111111"
    ],
    "whatsapp_code": "RK482",
    "visor_show_prices": false,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sales_office": {
      "id": "0199e3a0-0000-7000-8000-000000000301",
      "display_id": "B-2",
      "name": "Sala de ventas La Florida"
    },
    "unit_models": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000201",
        "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
        "project_id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "3D2B 78 m²",
        "description": "Esquina, con terraza en L.",
        "bedrooms": 3,
        "bathrooms": 2,
        "usable_area_m2": 72.5,
        "terrace_area_m2": 6,
        "total_area_m2": 78.5,
        "floor_plans": [
          "https://media.example.com/t/other/planta-3d2b.pdf"
        ],
        "renders": [
          "https://media.example.com/t/other/render-3d2b.jpg"
        ],
        "tour_360_url": "https://tour.example.com/3d2b",
        "created_by": null,
        "created_at": "2026-10-20T12:00:00.000Z",
        "updated_at": "2026-10-20T12:00:00.000Z"
      }
    ]
  }
}

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

PATCH /projects/{id}

Edit a proyecto

Partial update; null clears a field. Media fields (photos, brochure_url) are public URLs: upload the file with POST /tenant/media first and send its url. Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/projects/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "stage": "entrega_inmediata"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000101",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "name": "Parque Los Aromos",
    "description": "Condominio de departamentos a pasos del metro.",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "address": "Av. Vicuña Mackenna 0000",
    "stage": "en_verde",
    "delivery_date": "2027-12-01",
    "recepcion_definitiva_at": null,
    "on_call_user_ids": [],
    "subsidies": [
      "DS19"
    ],
    "sales_office_location_id": "0199e3a0-0000-7000-8000-000000000301",
    "photos": [
      "https://media.example.com/t/other/aromos-1.jpg"
    ],
    "brochure_url": "https://media.example.com/t/other/aromos-brochure.pdf",
    "meta_ad_ids": [
      "120211111111111111"
    ],
    "whatsapp_code": "RK482",
    "visor_show_prices": false,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sales_office": {
      "id": "0199e3a0-0000-7000-8000-000000000301",
      "display_id": "B-2",
      "name": "Sala de ventas La Florida"
    },
    "unit_models": [
      {
        "id": "0199e3a0-0000-7000-8000-000000000201",
        "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
        "project_id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "3D2B 78 m²",
        "description": "Esquina, con terraza en L.",
        "bedrooms": 3,
        "bathrooms": 2,
        "usable_area_m2": 72.5,
        "terrace_area_m2": 6,
        "total_area_m2": 78.5,
        "floor_plans": [
          "https://media.example.com/t/other/planta-3d2b.pdf"
        ],
        "renders": [
          "https://media.example.com/t/other/render-3d2b.jpg"
        ],
        "tour_360_url": "https://tour.example.com/3d2b",
        "created_by": null,
        "created_at": "2026-10-20T12:00:00.000Z",
        "updated_at": "2026-10-20T12:00:00.000Z"
      }
    ]
  }
}

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

GET /projects/{id}/document-requirements

Get a proyecto’s document checklist

What every buyer of this proyecto is asked for, in order. A proyecto that states none gets the platform default (cédula, liquidaciones, cotizaciones AFP, pre-aprobación; plus the subsidy certificate when the proyecto takes DS19/DS1/DS49) and is_default: true. reminder_days: how often buyers are reminded of missing items. Requires marketplace:read. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/document-requirements \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "items": [
      {
        "key": "carnet",
        "label": "Cédula de identidad (ambos lados)",
        "required": true
      },
      {
        "key": "liquidaciones",
        "label": "Últimas 3 liquidaciones de sueldo",
        "required": true
      }
    ],
    "is_default": false,
    "reminder_days": 3
  }
}

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

PUT /projects/{id}/document-requirements

Set a proyecto’s document checklist

Replaces the list (an empty list returns to the default). Existing negocios gain new items on their next read; items are never removed from a negocio. Requires marketplace:write. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
itemsany[]yes
reminder_daysinteger—≥ 0, ≤ 60
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/document-requirements \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "key": "carnet",
        "label": "Cédula de identidad (ambos lados)",
        "required": true
      },
      {
        "key": "liquidaciones",
        "label": "Últimas 3 liquidaciones de sueldo",
        "required": true
      }
    ],
    "reminder_days": 3
  }'

Example response (200)

{
  "data": {
    "items": [
      {
        "key": "carnet",
        "label": "Cédula de identidad (ambos lados)",
        "required": true
      },
      {
        "key": "liquidaciones",
        "label": "Últimas 3 liquidaciones de sueldo",
        "required": true
      }
    ],
    "is_default": false,
    "reminder_days": 3
  }
}

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

DELETE /projects/{id}/entrega-checklist

Back to the default entrega checklist

Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "custom": false,
    "sections": [
      {
        "key": "banos",
        "title": "Baños",
        "items": [
          {
            "key": "bano_artefactos",
            "label": "WC, lavamanos y ducha",
            "partida": "instalaciones_sanitarias"
          }
        ]
      }
    ]
  }
}

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

GET /projects/{id}/entrega-checklist

The proyecto’s entrega checklist

Its own list when it has one (custom: true), else the platform default. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/entrega-checklist \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "custom": false,
    "sections": [
      {
        "key": "banos",
        "title": "Baños",
        "items": [
          {
            "key": "bano_artefactos",
            "label": "WC, lavamanos y ducha",
            "partida": "instalaciones_sanitarias"
          }
        ]
      }
    ]
  }
}

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

PUT /projects/{id}/entrega-checklist

Set the proyecto’s own entrega checklist

Replaces the list as a whole; the next acta of the proyecto is born from it (actas already open keep theirs). Section and item keys are unique. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/entrega-checklist \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sections": [
      {
        "key": "banos",
        "title": "Baños",
        "items": [
          {
            "key": "bano_artefactos",
            "label": "WC, lavamanos y ducha",
            "partida": "instalaciones_sanitarias"
          }
        ]
      }
    ]
  }'

Example response (200)

{
  "data": {
    "custom": true,
    "sections": [
      {
        "key": "banos",
        "title": "Baños",
        "items": [
          {
            "key": "bano_artefactos",
            "label": "WC, lavamanos y ducha",
            "partida": "instalaciones_sanitarias"
          }
        ]
      }
    ]
  }
}

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

GET /projects/{id}/floor-plans

List a proyecto’s floor plans

The proyecto’s plans in display order (sort_order, then floor, then name), each with the unidades placed on it as polygons normalised to the image. The plans feed the embeddable floor-plan visor, which colours each placed unidad by its live status on the inmobiliaria’s own site; whether it shows prices is the proyecto’s visor_show_prices. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/floor-plans \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Piso 12",
      "floor": 12,
      "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
      "image_width": 1600,
      "image_height": 1000,
      "sort_order": 12,
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "units": [
        {
          "unit_id": "0199e3a0-0000-7000-8000-000000000401",
          "shape": [
            [
              0.1,
              0.2
            ],
            [
              0.3,
              0.2
            ],
            [
              0.3,
              0.45
            ],
            [
              0.1,
              0.45
            ]
          ]
        }
      ]
    }
  ]
}

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

POST /projects/{id}/floor-plans

Add a floor plan to a proyecto

A plan image (a floor, a planta tipo, a site plan of casas). Upload the image with POST /tenant/media first and send its url; send its pixel size too so the visor reserves the aspect ratio. The name is unique inside the proyecto (409 otherwise). Place unidades with PUT /floor-plans/\{id\}/units. The plans feed the embeddable floor-plan visor, which colours each placed unidad by its live status on the inmobiliaria’s own site; whether it shows prices is the proyecto’s visor_show_prices. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/floor-plans \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Piso 12",
    "floor": 12,
    "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
    "image_width": 1600,
    "image_height": 1000,
    "sort_order": 12
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "Piso 12",
    "floor": 12,
    "image_url": "https://media.example.com/t/other/aromos-piso-12.png",
    "image_width": 1600,
    "image_height": 1000,
    "sort_order": 12,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "units": [
      {
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "shape": [
          [
            0.1,
            0.2
          ],
          [
            0.3,
            0.2
          ],
          [
            0.3,
            0.45
          ],
          [
            0.1,
            0.45
          ]
        ]
      }
    ]
  }
}

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

GET /projects/{id}/marketing-kit

Read a proyecto’s marketing kit

The proyecto’s kit (listing copy, Instagram posts, WhatsApp broadcast message) and the source facts it is written from, so a person’s own agent can write copy in another voice and send it back with PATCH. kit is null before the first generation. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. A proyecto outside the caller’s proyectos is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/marketing-kit \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "kit": null,
    "source": {}
  }
}

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

PATCH /projects/{id}/marketing-kit

Edit a proyecto’s marketing kit

Replaces whole sections (listing, instagram, whatsapp); the others stay. whatsapp.message is one paragraph of at most 500 characters (it fills a template variable). 409 kit_missing before the first generation. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. A proyecto outside the caller’s proyectos is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/projects/<id>/marketing-kit \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "whatsapp": {
      "message": "Parque Los Aromos, en La Florida: en verde, entrega estimada marzo de 2027. Desde 2.890 UF. Escríbenos y coordinamos una visita a la sala de ventas."
    }
  }'

Example response (200)

{
  "data": {}
}

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

GET /projects/{id}/marketing-kit/audience

Who a kit broadcast would reach

Counts only, no people: the leads about the proyecto that inquired through Vitrina, how many were already reached this week (one kit broadcast per contact per 7 days), how many opted out of marketing, who would receive it, and whether the approved novedad_proyecto template is on the number. Portal, import, manual and campaign leads are never included. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. A proyecto outside the caller’s proyectos is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/marketing-kit/audience \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "matched": 120,
    "capped": 8,
    "opted_out": 5,
    "eligible": 107,
    "ready": true,
    "issue": null
  }
}

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

POST /projects/{id}/marketing-kit/broadcast

Send a proyecto’s kit message by WhatsApp

Sends the kit’s whatsapp.message to the leads of GET .../audience as ONE paced marketing campaign with the approved novedad_proyecto template, so STOP, contact hours and Meta’s limits apply and the results show in Campañas. A person’s decision: it never runs by itself. 409 kit_missing (no kit), no_channel / no_template (WhatsApp or the approved template is missing) or no_recipients. Requires campaigns:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. A proyecto outside the caller’s proyectos is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/marketing-kit/broadcast \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "campaign_id": "0199e3a0-0000-7000-8000-000000000c01",
    "recipients": 107
  }
}

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

POST /projects/{id}/marketing-kit/generate

Generate a proyecto’s marketing kit

Writes the kit from the proyecto’s data and media: name, comuna, etapa, entrega, subsidios, each tipología with its m² and cheapest disponible UF price, photos and renders. No model is used; the copy arranges facts the inmobiliaria already keeps, in the workspace’s tú / usted register. Generating again REPLACES the content, edits included. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. A proyecto outside the caller’s proyectos is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/marketing-kit/generate \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "kit": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "content": {
        "listing": {
          "headline": "Parque Los Aromos: departamentos en La Florida",
          "summary": "Parque Los Aromos, en La Florida. En verde, entrega estimada marzo de 2027. Desde 2.890 UF.",
          "body": "Departamentos pensados para vivir cerca del parque.\n\nTipologías disponibles:\n- 3D2B, 3 dormitorios, 2 baños, 78 m² desde 3.250 UF\n\nAgenda tu visita a la sala de ventas.",
          "highlights": [
            "Etapa: En verde",
            "Entrega estimada marzo de 2027",
            "Desde 2.890 UF"
          ]
        },
        "instagram": [
          {
            "caption": "Conoce Parque Los Aromos, en La Florida. En verde, entrega estimada marzo de 2027. Desde 2.890 UF. Agenda tu visita a la sala de ventas.",
            "hashtags": [
              "parquelosaromos",
              "laflorida",
              "departamentos"
            ],
            "image_url": "https://media.example.com/aromos/fachada.jpg"
          }
        ],
        "whatsapp": {
          "message": "Parque Los Aromos, en La Florida: en verde, entrega estimada marzo de 2027. Desde 2.890 UF. Escríbenos y coordinamos una visita a la sala de ventas."
        }
      },
      "generated_at": "2026-10-21T12:00:00.000Z",
      "generated_by": null,
      "edited_at": null,
      "edited_by": null,
      "created_at": "2026-10-21T12:00:00.000Z",
      "updated_at": "2026-10-21T12:00:00.000Z"
    },
    "source": {}
  }
}

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

GET /projects/{id}/members

List the ejecutivos of a proyecto

The members assigned to this proyecto, in the order they joined it. An ejecutivo on a role with project_visibility: assigned sees only the leads of the proyectos they are assigned to, and a new lead about the proyecto (lead.project_id) with no owner is owned by one of them, round-robin, through the assignment chain. Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/members \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "user_id": "0199e3a0-0000-7000-8000-000000000401",
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z"
    }
  ]
}

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

POST /projects/{id}/members

Assign an ejecutivo to a proyecto

Assigns an ACTIVE member of this workspace (400 otherwise). Idempotent: assigning someone already on the team returns their row. The member sees the proyecto’s leads on their next request and joins its lead rotation. Requires memberships:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
user_iduuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/members \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "0199e3a0-0000-7000-8000-000000000401"
  }'

Example response (201)

{
  "data": {
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "user_id": "0199e3a0-0000-7000-8000-000000000401",
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z"
  }
}

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

DELETE /projects/{id}/members/{userId}

Unassign an ejecutivo from a proyecto

Takes the member off the proyecto’s team: they stop seeing its leads (except the ones they own) and leave its rotation. 404 when they were not on it. Requires memberships:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "user_id": "0199e3a0-0000-7000-8000-000000000401",
    "deleted": true
  }
}

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

GET /projects/{id}/milestone-terms

Get a proyecto’s milestone deadlines

The default deadlines a new negocio of this proyecto gets: the promesa promesa_due_days after the reserva (default 30), the escritura escritura_due_days after the promesa and the entrega entrega_due_days after the escritura (null = no default). Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/milestone-terms \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "promesa_due_days": 30,
    "escritura_due_days": 180,
    "entrega_due_days": 60
  }
}

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

PUT /projects/{id}/milestone-terms

Set a proyecto’s milestone deadlines

Applies to deadlines set afterwards (a new negocio, a promesa or escritura recorded later); existing deadlines are not moved. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/milestone-terms \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "promesa_due_days": 30,
    "escritura_due_days": 180,
    "entrega_due_days": 60
  }'

Example response (200)

{
  "data": {
    "promesa_due_days": 30,
    "escritura_due_days": 180,
    "entrega_due_days": 60
  }
}

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

GET /projects/{id}/obra-updates

A proyecto’s obra progress posts

Newest first. Requires marketplace:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000e01",
      "project_id": "0199e3a0-0000-7000-8000-000000000a01",
      "title": "Terminaciones piso 6",
      "note": "Se instalaron los pisos flotantes y se pintó el hall.",
      "photos": [
        {
          "index": 0,
          "filename": "hall.jpg",
          "mime": "image/jpeg",
          "size": 482113
        }
      ],
      "posted_by": "0199e3a0-0000-7000-8000-000000000f01",
      "notified_at": null,
      "notified_count": 0,
      "created_at": "2026-10-22T14:05:00.000Z"
    }
  ]
}

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

POST /projects/{id}/obra-updates

Post an obra progress update

A title and a note; add photos with …/photos (up to 8), then tell the buyers with …/announce. Buyers can ask for the latest post and its photos on WhatsApp at any time. Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
titlestringyesmín. 1, máx. 140
notestring | null—máx. 2000
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Terminaciones piso 6",
    "note": "Se instalaron los pisos flotantes."
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000e01",
    "project_id": "0199e3a0-0000-7000-8000-000000000a01",
    "title": "Terminaciones piso 6",
    "note": "Se instalaron los pisos flotantes y se pintó el hall.",
    "photos": [
      {
        "index": 0,
        "filename": "hall.jpg",
        "mime": "image/jpeg",
        "size": 482113
      }
    ],
    "posted_by": "0199e3a0-0000-7000-8000-000000000f01",
    "notified_at": null,
    "notified_count": 0,
    "created_at": "2026-10-22T14:05:00.000Z"
  }
}

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

DELETE /projects/{id}/obra-updates/{updateId}

Remove an obra post

Buyers no longer see it. Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
updateIdpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /projects/{id}/obra-updates/{updateId}/announce

Tell the proyecto’s buyers about an obra post

A WhatsApp message to every buyer with a live negocio in the proyecto, inside Meta’s 24 h window only: a buyer whose window is closed is counted in window_closed, not messaged, and can still ask for the post. Once per post (409 obra_already_announced). Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
updateIdpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates/<id>/announce \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "buyers": 14,
    "sent": 5,
    "window_closed": 9,
    "other": 0
  }
}

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

POST /projects/{id}/obra-updates/{updateId}/photos

Add a photo to an obra post

multipart/form-data with one file part (or the JSON file shape), JPG, PNG or WebP up to 10 MB; at most 8 photos per post. Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
updateIdpathuuidyes

Body

FieldTypeRequiredConstraints
filenamestringyesmín. 1, máx. 255
contentstringyesmín. 1, máx. 35000000
content_encodingutf8 \base64—
content_typestring—máx. 200
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates/<id>/photos \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "hall.jpg",
    "content": "/9j/4AAQSkZJRg…",
    "content_encoding": "base64"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000e01",
    "project_id": "0199e3a0-0000-7000-8000-000000000a01",
    "title": "Terminaciones piso 6",
    "note": "Se instalaron los pisos flotantes y se pintó el hall.",
    "photos": [
      {
        "index": 0,
        "filename": "hall.jpg",
        "mime": "image/jpeg",
        "size": 482113
      }
    ],
    "posted_by": "0199e3a0-0000-7000-8000-000000000f01",
    "notified_at": null,
    "notified_count": 0,
    "created_at": "2026-10-22T14:05:00.000Z"
  }
}

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

DELETE /projects/{id}/obra-updates/{updateId}/photos/{index}

Remove a photo from an obra post

Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
updateIdpathuuidyes
indexpathinteger | nullno≥ 0, ≤ 7
curl -X DELETE https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates/<id>/photos/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000e01",
    "project_id": "0199e3a0-0000-7000-8000-000000000a01",
    "title": "Terminaciones piso 6",
    "note": "Se instalaron los pisos flotantes y se pintó el hall.",
    "photos": [
      {
        "index": 0,
        "filename": "hall.jpg",
        "mime": "image/jpeg",
        "size": 482113
      }
    ],
    "posted_by": "0199e3a0-0000-7000-8000-000000000f01",
    "notified_at": null,
    "notified_count": 0,
    "created_at": "2026-10-22T14:05:00.000Z"
  }
}

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

GET /projects/{id}/obra-updates/{updateId}/photos/{index}

Read an obra photo

The image bytes. Requires marketplace:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
updateIdpathuuidyes
indexpathinteger | nullno≥ 0, ≤ 7
curl https://api.vitrinadev.com/api/v1/projects/<id>/obra-updates/<id>/photos/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /projects/{id}/pie-terms

Get a proyecto’s cuota reminders

When buyers are reminded of a pie cuota. Requires marketplace:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/pie-terms \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "reminder_days_before": 3,
    "reminder_days_after": 2
  }
}

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

PUT /projects/{id}/pie-terms

Set a proyecto’s cuota reminders

Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/pie-terms \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reminder_days_before": 3,
    "reminder_days_after": 2
  }'

Example response (200)

{
  "data": {
    "reminder_days_before": 3,
    "reminder_days_after": 2
  }
}

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

GET /projects/{id}/publication-readiness

Check a proyecto before publishing it to Portal Inmobiliario

Dry run: what the portal would refuse, with nothing sent. field_errors is keyed by the field to fix (a proyecto field, models.&lt;tipología&gt;.&lt;field&gt;, price, comuna, photos, seller_contact). A tipología needs its dormitorios, baños, superficies (útil, terraza, total) and a price in the published lista; its orientation, floor and estacionamientos come from its unidades, and gastos comunes from the proyecto’s unidades. Requires marketplace:read. The proyecto is ONE listing in the portal’s «Proyectos» category and each tipología a variation of it, with the «desde» price (UF, from the published lista de precios) and the stock (disponible unidades) of that tipología. The listing is the anuncio that maps inbound leads to the proyecto, so its leads land on the proyecto with no mapping step. Only on the Proyectos module.

ParameterInTypeRequiredConstraints
idpathuuidyes
account_idqueryuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/publication-readiness \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "ready": false,
    "field_errors": {
      "models.3D2B.orientation": "Falta «Orientación (de sus unidades)» en la tipología «3D2B»."
    },
    "models_listed": 3,
    "units_available": 41,
    "price_from_uf": 2890
  }
}

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

GET /projects/{id}/publications

List a proyecto’s Portal Inmobiliario anuncios

The anuncios of the proyecto with the portal’s state (portal_state), the last push and its error. Requires marketplace:read. The proyecto is ONE listing in the portal’s «Proyectos» category and each tipología a variation of it, with the «desde» price (UF, from the published lista de precios) and the stock (disponible unidades) of that tipología. The listing is the anuncio that maps inbound leads to the proyecto, so its leads land on the proyecto with no mapping step. Only on the Proyectos module.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/publications \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "provider": "mercadolibre",
      "external_id": "MLC1234567890",
      "title": "Parque Central",
      "permalink": "https://departamento.mercadolibre.cl/MLC-1234567890-parque-central-_JM",
      "origin": "vitrina",
      "status": "published",
      "listing_type": "gold_premium",
      "portal_state": "listed",
      "portal_state_at": "2026-10-20T12:00:00.000Z",
      "paused_by_vitrina": false,
      "tipologias_listed": 1,
      "last_published_at": "2026-10-20T12:00:00.000Z",
      "last_synced_at": "2026-10-20T12:00:00.000Z",
      "last_error": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  ]
}

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

POST /projects/{id}/publications

Publish a proyecto and its tipologías to Portal Inmobiliario

Lists the proyecto, or refreshes its live listing (outcome: updated). After that the listing follows the proyecto: an edit of the proyecto, a tipología or a unidad, and a new published lista de precios, push the content (price «desde» and stock per tipología); when no unidad is disponible the listing is paused, and resumed when stock returns; removing the proyecto closes it (final on the portal). A pause or close on the portal is recorded as portal_state and never changes a unidad’s status. Errors (422), each with field_errors: portal_listing_incomplete (nothing was sent), portal_no_package (the account has no developments pack with cupo, or lacks the developments permission: each pack allows one publication), portal_listing_rejected (the portal refused it; details.causes verbatim). Requires marketplace:write. The proyecto is ONE listing in the portal’s «Proyectos» category and each tipología a variation of it, with the «desde» price (UF, from the published lista de precios) and the stock (disponible unidades) of that tipología. The listing is the anuncio that maps inbound leads to the proyecto, so its leads land on the proyecto with no mapping step. Only on the Proyectos module.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/projects/<id>/publications \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "0199e3a0-0000-7000-8000-000000000601"
  }'

Example response (201)

{
  "data": {
    "outcome": "published",
    "publication": {
      "id": "0199e3a0-0000-7000-8000-000000000501",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "provider": "mercadolibre",
      "external_id": "MLC1234567890",
      "title": "Parque Central",
      "permalink": "https://departamento.mercadolibre.cl/MLC-1234567890-parque-central-_JM",
      "origin": "vitrina",
      "status": "published",
      "listing_type": "gold_premium",
      "portal_state": "listed",
      "portal_state_at": "2026-10-20T12:00:00.000Z",
      "paused_by_vitrina": false,
      "tipologias_listed": 1,
      "last_published_at": "2026-10-20T12:00:00.000Z",
      "last_synced_at": "2026-10-20T12:00:00.000Z",
      "last_error": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  }
}

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

DELETE /projects/{id}/publications/{publicationId}

Close a proyecto’s listing on Portal Inmobiliario

Closes the listing on the portal (final there: publishing again creates a new one). The proyecto and its unidades are untouched. Requires marketplace:write. The proyecto is ONE listing in the portal’s «Proyectos» category and each tipología a variation of it, with the «desde» price (UF, from the published lista de precios) and the stock (disponible unidades) of that tipología. The listing is the anuncio that maps inbound leads to the proyecto, so its leads land on the proyecto with no mapping step. Only on the Proyectos module.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000501",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "marketplace_account_id": "0199e3a0-0000-7000-8000-000000000601",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "provider": "mercadolibre",
    "external_id": "MLC1234567890",
    "title": "Parque Central",
    "permalink": "https://departamento.mercadolibre.cl/MLC-1234567890-parque-central-_JM",
    "origin": "vitrina",
    "status": "deleted",
    "listing_type": "gold_premium",
    "portal_state": "withdrawn",
    "portal_state_at": "2026-10-20T12:00:00.000Z",
    "paused_by_vitrina": false,
    "tipologias_listed": 1,
    "last_published_at": "2026-10-20T12:00:00.000Z",
    "last_synced_at": "2026-10-20T12:00:00.000Z",
    "last_error": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

GET /projects/{id}/reserva-terms

Get a proyecto’s reserva terms

What a reserva of this proyecto costs (amount in currency, UF or CLP; null = stated on each reserva) and how long it holds the unidades bloqueada (hold_hours, default 48). Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/reserva-terms \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "amount": 20,
    "currency": "UF",
    "hold_hours": 48
  }
}

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

PUT /projects/{id}/reserva-terms

Set a proyecto’s reserva terms

Sets the default reserva amount (UF or CLP; both null to state it on each reserva) and the hold in hours (1–720). Applies to reservas started afterwards. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/reserva-terms \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 20,
    "currency": "UF",
    "hold_hours": 48
  }'

Example response (200)

{
  "data": {
    "amount": 20,
    "currency": "UF",
    "hold_hours": 48
  }
}

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

DELETE /projects/{id}/signing-templates/{kind}

Reset a proyecto’s signing template

Back to the platform text. Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
kindpathorden_reservayes
curl -X DELETE https://api.vitrinadev.com/api/v1/projects/<id>/signing-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "kind": "orden_reserva",
    "title": "Orden de reserva",
    "body": "# Orden de reserva\n\nEn {{ciudad}}, el {{fecha}}, {{comprador_nombre}} reserva…",
    "is_default": true,
    "placeholders": [
      "inmobiliaria",
      "proyecto",
      "comprador_nombre",
      "unidades",
      "precio_total"
    ]
  }
}

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

GET /projects/{id}/signing-templates/{kind}

Get a proyecto’s signing template

The text of a signable document (the orden de reserva) with its {{placeholders}}; is_default when the proyecto uses the platform text. Requires marketplace:read. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
kindpathorden_reservayes
curl https://api.vitrinadev.com/api/v1/projects/<id>/signing-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "kind": "orden_reserva",
    "title": "Orden de reserva",
    "body": "# Orden de reserva\n\nEn {{ciudad}}, el {{fecha}}, {{comprador_nombre}} reserva…",
    "is_default": true,
    "placeholders": [
      "inmobiliaria",
      "proyecto",
      "comprador_nombre",
      "unidades",
      "precio_total"
    ]
  }
}

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

PUT /projects/{id}/signing-templates/{kind}

Set a proyecto’s signing template

Title and body (headings with #, paragraphs, {{placeholders}}); it must name the buyer ({{comprador_nombre}}). Applies to documents generated afterwards; a request already sent keeps its PDF. Requires marketplace:write. Only on a workspace that runs the Proyectos module; an ejecutivo only reaches their proyectos’ negocios (404 otherwise).

ParameterInTypeRequiredConstraints
idpathuuidyes
kindpathorden_reservayes

Body

FieldTypeRequiredConstraints
titlestringyesmín. 1, máx. 120
bodystringyesmín. 20, máx. 20000
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/signing-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Orden de reserva",
    "body": "# Orden de reserva\n\nEn {{ciudad}}, el {{fecha}}, {{comprador_nombre}} reserva…"
  }'

Example response (200)

{
  "data": {
    "kind": "orden_reserva",
    "title": "Orden de reserva",
    "body": "# Orden de reserva\n\nEn {{ciudad}}, el {{fecha}}, {{comprador_nombre}} reserva…",
    "is_default": false,
    "placeholders": [
      "inmobiliaria",
      "proyecto",
      "comprador_nombre",
      "unidades",
      "precio_total"
    ]
  }
}

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

GET /projects/{id}/toctoc

A proyecto’s TocToc publishing payload and state

The payload a hand-over of the proyecto to TocToc carries (the proyecto, its tipologías and the live stock behind each, versioned by schema), what the listing still lacks (issues, empty = ready), and where the proyecto stands on TocToc (publication, null = a draft). Requires marketplace:read. TocToc offers no API or webhook (spike #4427): its lead notifications arrive by the forwarded email at the workspace’s lead door, and publishing is assisted (export the payload, record the code TocToc returns). Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/toctoc \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "publication": {
      "id": "0199e3a0-0000-7000-8000-000000000803",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "status": "live",
      "external_ref": "TT-48213",
      "notes": null,
      "submitted_at": "2026-10-20T12:00:00.000Z",
      "live_at": "2026-10-20T12:00:00.000Z",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    },
    "payload": {
      "schema": "vitrina.toctoc.project.v1",
      "project": {
        "reference": "0199e3a0-0000-7000-8000-000000000101",
        "name": "Parque Los Aromos",
        "description": "Condominio de departamentos a pasos del metro.",
        "stage": "en_verde",
        "stage_label": "En verde",
        "delivery_date": "2027-12-01",
        "region": "Metropolitana",
        "comuna": "La Florida",
        "address": "Av. Vicuña Mackenna 0000",
        "subsidies": [
          "DS19"
        ],
        "photos": [
          "https://media.example.com/t/other/aromos-1.jpg"
        ],
        "brochure_url": null
      },
      "unit_models": [
        {
          "reference": "0199e3a0-0000-7000-8000-000000000201",
          "name": "3D2B 78 m²",
          "description": null,
          "bedrooms": 3,
          "bathrooms": 2,
          "usable_area_m2": 72.5,
          "terrace_area_m2": 6,
          "total_area_m2": 78.5,
          "floor_plans": [],
          "renders": [],
          "tour_360_url": null,
          "total_units": 24,
          "available_units": 11,
          "price": {
            "currency": "UF",
            "from": 3450,
            "to": 3780
          }
        }
      ],
      "extras": {
        "parking_available": 8,
        "storage_available": 5
      }
    },
    "issues": [
      {
        "field": "project.brochure_url",
        "message": "Agrega el brochure del proyecto."
      }
    ]
  }
}

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

PUT /projects/{id}/toctoc

Record where a proyecto stands on TocToc

Records draft, submitted (handed to TocToc), live or paused, and the code TocToc gave the proyecto. live needs the code (400 otherwise). Recording a code maps it to the proyecto, so the leads that carry it land on it and those already waiting on it are moved; a code that belongs to another proyecto is refused (400). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PUT https://api.vitrinadev.com/api/v1/projects/<id>/toctoc \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "live",
    "external_ref": "TT-48213"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000803",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "status": "live",
    "external_ref": "TT-48213",
    "notes": null,
    "submitted_at": "2026-10-20T12:00:00.000Z",
    "live_at": "2026-10-20T12:00:00.000Z",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

Get the tracked WhatsApp links of a proyecto

One wa.me link per enabled WhatsApp number of the workspace, each prefilling a message that carries the proyecto’s whatsapp_code. A buyer who opens it and sends that message starts a conversation tied to the proyecto: the agent opens on it by name, and the lead is about it (lead.project_id, owned by the proyecto’s ejecutivos) with the link in its notes. Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/projects/<id>/whatsapp-links \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "whatsapp_code": "RK482",
    "prefill_text": "Hola, me interesa el proyecto Parque Los Aromos (código RK482)",
    "links": [
      {
        "messaging_account_id": "0199e3a0-0000-7000-8000-000000000501",
        "name": "WhatsApp ventas",
        "phone_e164": "+56912345678",
        "url": "https://wa.me/56912345678?text=Hola%2C%20me%20interesa%20el%20proyecto%20Parque%20Los%20Aromos%20(c%C3%B3digo%20RK482)"
      }
    ]
  }
}

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

GET /real-estate-offers

List the unit offers sent to old leads

Each time a unidad that matches an old lead's criteria became available, dropped in price or was released after a reserva fell through, and the workspace had the offers on, the lead was offered it; this is that ledger, newest first. A caller whose role narrows proyectos sees the offers of their proyectos only. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/real-estate-offers \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [],
  "meta": {
    "pagination": {
      "total": 0,
      "limit": 50,
      "offset": 0
    }
  }
}

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

GET /real-estate-offers/settings

Read the unit-offer settings

Whether leads that inquired hear about matching stock, how many offers one contact may get per week, and how old an inquiry may be. Off by default. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl https://api.vitrinadev.com/api/v1/real-estate-offers/settings \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "enabled": false,
    "weekly_cap": 1,
    "max_inquiry_age_days": 365
  }
}

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

PUT /real-estate-offers/settings

Change the unit-offer settings

The owner's decision to let old leads hear about matching stock (templates, STOP respected, capped per week). Only leads that inquired through Vitrina are ever offered a unidad, never portal or imported history. Turning it on starts offers for stock news from then on, with no backlog. Requires campaigns:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X PUT https://api.vitrinadev.com/api/v1/real-estate-offers/settings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "weekly_cap": 1
  }'

Example response (200)

{
  "data": {
    "enabled": true,
    "weekly_cap": 1,
    "max_inquiry_age_days": 365
  }
}

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

GET /real-estate-reports/projects

Developer report per proyecto

For an inmobiliaria: per proyecto and in total, the funnel by milestone (leads that arrived in the period, how many reached each step), the stock by status, absorption and sales velocity (reservas per month, months to sell out), desistimientos by reason, and an ejecutivo ranking. Filters: project_id, sales_office_location_id (a sala de ventas), from / to. A caller whose role narrows proyectos sees only the figures of their proyectos. Only on a workspace that runs the Proyectos module; any other answers 403 MODULE_NOT_ENABLED. Requires marketplace:read.

ParameterInTypeRequiredConstraints
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
project_idqueryuuidno
sales_office_location_idqueryuuidno
pipeline_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/real-estate-reports/projects \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "pipeline_id": "0199e3a0-0000-7000-8000-000000000301",
    "period": {
      "from": "2026-09-21",
      "to": "2026-10-20",
      "timezone": "America/Santiago"
    },
    "filters": {
      "project_id": null,
      "sales_office_id": null
    },
    "totals": {
      "funnel": {
        "cohort": 120,
        "steps": [
          {
            "key": "lead",
            "name": "Lead",
            "reached": 120,
            "conversion_from_previous": null
          },
          {
            "key": "visit_scheduled",
            "name": "Visita agendada",
            "reached": 54,
            "conversion_from_previous": 0.45
          }
        ],
        "lost": 18
      },
      "stock": {
        "by_status": {
          "disponible": 140,
          "bloqueada": 2,
          "reservada": 9,
          "promesada": 12,
          "vendida": 37,
          "pausada": 0
        },
        "dwellings": 200,
        "extras_by_status": {
          "disponible": 80,
          "bloqueada": 0,
          "reservada": 3,
          "promesada": 4,
          "vendida": 13,
          "pausada": 0
        },
        "extras": 100,
        "available_value_uf": 512340.5
      },
      "absorption": {
        "on_sale": 200,
        "committed": 58,
        "absorption_rate": 0.29,
        "sold_rate": 0.185,
        "reservations": 11,
        "reservations_per_month": 11.2,
        "months_to_sell_out": 12.5,
        "by_month": [
          {
            "month": "2026-10",
            "reservations": 11
          }
        ]
      },
      "desistimientos": {
        "total": 4,
        "by_reason": [
          {
            "reason": "financing_rejected",
            "count": 3
          },
          {
            "reason": "price",
            "count": 1
          }
        ]
      }
    },
    "projects": [],
    "ejecutivos": []
  }
}

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

GET /reservas

List reservas

Newest first, with what has been paid. Filters: project_id, status, lead_id, cotizacion_id. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
statusquerypending \paid \expired \
lead_idqueryuuidno
cotizacion_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/reservas \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "pending",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": null,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": null,
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null,
      "paid_clp": 0,
      "outstanding_clp": 790642,
      "payment_state": "pending",
      "is_overdue": false
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

GET /reservas/{id}

Get a reserva

One reserva: the unidades it holds, the deadline, the amount, what is paid and what is left, and the negocio it opened once paid. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/reservas/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a01",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "cotizacion_display_id": "CZ-12",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "co_buyer_contact_ids": [],
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "property_ids": [
      "0199e3a0-0000-7000-8000-000000000401",
      "0199e3a0-0000-7000-8000-000000000402"
    ],
    "status": "pending",
    "amount": 20,
    "currency": "UF",
    "amount_clp": 790642,
    "uf_value_clp": 39532.12,
    "expires_at": "2026-10-22T15:00:00.000Z",
    "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
    "negocio_id": null,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T15:00:00.000Z",
    "updated_at": "2026-10-20T15:00:00.000Z",
    "paid_at": null,
    "expired_at": null,
    "cancelled_at": null,
    "cancelled_reason": null,
    "paid_clp": 0,
    "outstanding_clp": 790642,
    "payment_state": "pending",
    "is_overdue": false
  }
}

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

POST /reservas/{id}/cancel

Release a reserva’s hold

A pending reserva only: the unidades return to disponible, the cobro is cancelled (money already paid stays on it for the business to return or apply) and the open payment link stops taking money. 409 reserva_not_pending, reserva_already_paid. Requires leads:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
reasonstringyesmín. 1, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/reservas/<id>/cancel \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "El comprador desistió antes de pagar"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000a01",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "project_name": "Parque Los Aromos",
    "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
    "cotizacion_display_id": "CZ-12",
    "lead_id": "0199e3a0-0000-7000-8000-000000000301",
    "contact_id": "0199e3a0-0000-7000-8000-000000000302",
    "co_buyer_contact_ids": [],
    "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
    "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
    "property_ids": [
      "0199e3a0-0000-7000-8000-000000000401",
      "0199e3a0-0000-7000-8000-000000000402"
    ],
    "status": "cancelled",
    "amount": 20,
    "currency": "UF",
    "amount_clp": 790642,
    "uf_value_clp": 39532.12,
    "expires_at": "2026-10-22T15:00:00.000Z",
    "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
    "negocio_id": null,
    "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
    "created_by_kind": "ui_member",
    "created_at": "2026-10-20T15:00:00.000Z",
    "updated_at": "2026-10-20T15:00:00.000Z",
    "paid_at": null,
    "expired_at": null,
    "cancelled_at": "2026-10-21T10:00:00.000Z",
    "cancelled_reason": "El comprador desistió antes de pagar",
    "paid_clp": 0,
    "outstanding_clp": 0,
    "payment_state": "cancelled",
    "is_overdue": false
  }
}

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

GET /reservas/{id}/payment

How a reserva can be paid

For a pending reserva: the payment link (created on demand, expiring with the hold) and/or the transfer accounts, by the workspace’s charging preference. 409 reserva_not_pending otherwise. Requires leads:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. An ejecutivo only reaches their proyectos’ reservas and negocios; anything else is a 404.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/reservas/<id>/payment \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "reserva": {
      "id": "0199e3a0-0000-7000-8000-000000000a01",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "project_name": "Parque Los Aromos",
      "cotizacion_id": "0199e3a0-0000-7000-8000-000000000901",
      "cotizacion_display_id": "CZ-12",
      "lead_id": "0199e3a0-0000-7000-8000-000000000301",
      "contact_id": "0199e3a0-0000-7000-8000-000000000302",
      "co_buyer_contact_ids": [],
      "owner_user_id": "0199e3a0-0000-7000-8000-0000000000bb",
      "conversation_id": "0199e3a0-0000-7000-8000-000000000e01",
      "property_ids": [
        "0199e3a0-0000-7000-8000-000000000401",
        "0199e3a0-0000-7000-8000-000000000402"
      ],
      "status": "pending",
      "amount": 20,
      "currency": "UF",
      "amount_clp": 790642,
      "uf_value_clp": 39532.12,
      "expires_at": "2026-10-22T15:00:00.000Z",
      "obligation_id": "0199e3a0-0000-7000-8000-000000000c01",
      "negocio_id": null,
      "created_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "created_by_kind": "ui_member",
      "created_at": "2026-10-20T15:00:00.000Z",
      "updated_at": "2026-10-20T15:00:00.000Z",
      "paid_at": null,
      "expired_at": null,
      "cancelled_at": null,
      "cancelled_reason": null,
      "paid_clp": 0,
      "outstanding_clp": 790642,
      "payment_state": "pending",
      "is_overdue": false
    },
    "payment": {
      "mode": "mp_first",
      "link": {
        "checkout_id": "0199e3a0-0000-7000-8000-000000000d01",
        "checkout_url": "https://api.vitrinadev.com/p/0199e3a0-0000-7000-8000-000000000d01",
        "amount_clp": 790642
      },
      "transfer_accounts": [
        {
          "alias": "Cuenta corriente",
          "bank": "Banco de Chile",
          "account_type": "corriente",
          "account_number": "00123456789",
          "holder_name": "Inmobiliaria Los Aromos SpA",
          "holder_rut": "76.123.456-7"
        }
      ]
    }
  }
}

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

GET /toctoc-connection

The TocToc lead door of the workspace

Whether the inbound TocToc lead door exists, its last lead and the path it is served on. Requires marketplace:read. TocToc offers no API or webhook (spike #4427): its lead notifications arrive by the forwarded email at the workspace’s lead door, and publishing is assisted (export the payload, record the code TocToc returns). Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

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

Example response (200)

{
  "data": {
    "connected": true,
    "connection": {
      "id": "0199e3a0-0000-7000-8000-000000000802",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "token_hint": "a1b2",
      "last_lead_at": "2026-10-20T12:00:00.000Z",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    },
    "lead_path": "/api/v1/integrations/toctoc/leads"
  }
}

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

POST /toctoc-connection/token

Create or rotate the TocToc lead door token

Creates the workspace's lead door, or rotates its token (the previous URL stops working at once). The token is returned ONCE: only its hash is kept. POST the forwarded TocToc notification to path, as JSON: either a lead (name/nombre, email/correo, phone/telefono, message/mensaje, project_code/codigo, project_name/proyecto) or the email (\{ "email": \{ "from", "subject", "text", "html", "message_id" \} \}). A body that yields no name, email or phone is kept for a person to read and creates no lead. Requires marketplace:write. TocToc offers no API or webhook (spike #4427): its lead notifications arrive by the forwarded email at the workspace’s lead door, and publishing is assisted (export the payload, record the code TocToc returns). Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

curl -X POST https://api.vitrinadev.com/api/v1/toctoc-connection/token \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "connection": {
      "id": "0199e3a0-0000-7000-8000-000000000802",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "token_hint": "a1b2",
      "last_lead_at": "2026-10-20T12:00:00.000Z",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    },
    "token": "tt_0f3c…a1b2",
    "path": "/api/v1/integrations/toctoc/leads/tt_0f3c…a1b2"
  }
}

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

GET /toctoc-listings

List TocToc publications and their proyecto

The TocToc publication codes seen on leads or recorded on a proyecto, with how many leads arrived on each. mapped=false is the queue of publications waiting for a proyecto: their leads were created with no proyecto and a note. Requires marketplace:read. TocToc offers no API or webhook (spike #4427): its lead notifications arrive by the forwarded email at the workspace’s lead door, and publishing is assisted (export the payload, record the code TocToc returns). Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

ParameterInTypeRequiredConstraints
mappedquerytrue \falseno
project_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/toctoc-listings \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000801",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "external_id": "TT-48213",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "title": "Parque Los Aromos",
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "lead_count": 4,
      "unmapped_lead_count": 0
    }
  ]
}

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

POST /toctoc-listings

Map a TocToc publication code to a proyecto

Maps the code TocToc gave a publication to a proyecto ahead of its first lead. Idempotent on the code: posting an already-seen one re-maps it, stamps the proyecto on its waiting leads and routes the unowned ones to the proyecto's ejecutivos. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

curl -X POST https://api.vitrinadev.com/api/v1/toctoc-listings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "external_id": "TT-48213"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000801",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "external_id": "TT-48213",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "title": "Parque Los Aromos",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "leads_mapped": 0
  }
}

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

PATCH /toctoc-listings/{id}

Map or unmap a TocToc publication

Sets the proyecto of a publication (project_id), or clears it with null. Mapping stamps the proyecto on the leads waiting on the publication and routes the unowned ones to its ejecutivos; unmapping only affects leads that arrive afterwards. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED. Refused (403) to a caller whose role narrows proyectos.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/toctoc-listings/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000801",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "external_id": "TT-48213",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "title": "Parque Los Aromos",
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "leads_mapped": 3
  }
}

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

GET /unit-models

List tipologías

Live tipologías of live proyectos, grouped by proyecto (newest first) then by name. Filters ANDed: project_id, the proyecto’s comuna and stage (etapa), min_bedrooms, min_bathrooms, free text q. Each row names its proyecto. Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
comunaquerystring[]no
stagequeryen_blanco \en_verde \entrega_inmediata[]
min_bedroomsqueryinteger | nullno≥ 0, ≤ 99
min_bathroomsqueryinteger | nullno≥ 0, ≤ 99
qquerystringnomáx. 200
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/unit-models \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000201",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "3D2B 78 m²",
      "description": "Esquina, con terraza en L.",
      "bedrooms": 3,
      "bathrooms": 2,
      "usable_area_m2": 72.5,
      "terrace_area_m2": 6,
      "total_area_m2": 78.5,
      "floor_plans": [
        "https://media.example.com/t/other/planta-3d2b.pdf"
      ],
      "renders": [
        "https://media.example.com/t/other/render-3d2b.jpg"
      ],
      "tour_360_url": "https://tour.example.com/3d2b",
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "project": {
        "id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "Parque Los Aromos",
        "comuna": "La Florida",
        "stage": "en_verde"
      }
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /unit-models

Add a tipología to a proyecto

Names are unique among the proyecto’s live tipologías (409 otherwise). floor_plans (plantas), renders and tour_360_url are public URLs: upload files with POST /tenant/media first. Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/unit-models \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "3D2B 78 m²",
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000201",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "3D2B 78 m²",
    "description": "Esquina, con terraza en L.",
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "floor_plans": [
      "https://media.example.com/t/other/planta-3d2b.pdf"
    ],
    "renders": [
      "https://media.example.com/t/other/render-3d2b.jpg"
    ],
    "tour_360_url": "https://tour.example.com/3d2b",
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

DELETE /unit-models/{id}

Remove a tipología

Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

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

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000201",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "3D2B 78 m²",
    "deleted": true
  }
}

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

GET /unit-models/{id}

One tipología

Requires marketplace:read. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/unit-models/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000201",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "3D2B 78 m²",
    "description": "Esquina, con terraza en L.",
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "floor_plans": [
      "https://media.example.com/t/other/planta-3d2b.pdf"
    ],
    "renders": [
      "https://media.example.com/t/other/render-3d2b.jpg"
    ],
    "tour_360_url": "https://tour.example.com/3d2b",
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

PATCH /unit-models/{id}

Edit a tipología

Partial update; null clears a field. A tipología stays in its proyecto. Requires marketplace:write. A caller whose role narrows proyectos (project_visibility assigned = ejecutivo, sales_office = jefe de sala) only sees the proyectos it admits; any other answers 404, as if it did not exist. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X PATCH https://api.vitrinadev.com/api/v1/unit-models/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bedrooms": 3
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000201",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "name": "3D2B 78 m²",
    "description": "Esquina, con terraza en L.",
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "floor_plans": [
      "https://media.example.com/t/other/planta-3d2b.pdf"
    ],
    "renders": [
      "https://media.example.com/t/other/render-3d2b.jpg"
    ],
    "tour_360_url": "https://tour.example.com/3d2b",
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z"
  }
}

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

GET /units

List unidades (a proyecto’s stock)

Live unidades of live proyectos, by floor, then kind, then number in natural order. Filters ANDed: project_id, unit_model_id, kind, status, min_floor / max_floor, min_bedrooms, free text q. meta.status_counts counts the same filters WITHOUT status, for a grid’s status chips. A unidad is a property row: GET /properties lists unidades too with include_units=true. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
project_idqueryuuid[]no
unit_model_idqueryuuid[]no
kindquerydepartamento \casa \estacionamiento \
statusquerydisponible \bloqueada \reservada \
min_floorqueryinteger | nullno≥ -10, ≤ 300
max_floorqueryinteger | nullno≥ -10, ≤ 300
min_bedroomsqueryinteger | nullno≥ 0, ≤ 99
qquerystringnomáx. 200
limitqueryintegerno≥ 1, ≤ 500
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/units \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "0199e3a0-0000-7000-8000-000000000401",
      "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
      "display_id": "I-42",
      "title": "Departamento 1203 · Parque Los Aromos",
      "description": null,
      "operation": "venta",
      "property_type": "departamento",
      "kind": "departamento",
      "status": "disponible",
      "region": "Metropolitana",
      "comuna": "La Florida",
      "sector": null,
      "address": null,
      "sale_price_amount": 3250.5,
      "sale_price_currency": "UF",
      "rent_price_amount": null,
      "rent_price_currency": null,
      "common_expenses_clp": null,
      "property_tax_clp": null,
      "bedrooms": 3,
      "bathrooms": 2,
      "usable_area_m2": 72.5,
      "terrace_area_m2": 6,
      "total_area_m2": 78.5,
      "parking_spaces": null,
      "storage_units": null,
      "orientation": "nororiente",
      "floor": 12,
      "year_built": null,
      "furnished": null,
      "pets_allowed": null,
      "photos": [],
      "broker_user_id": null,
      "owner_name": null,
      "owner_phone": null,
      "owner_email": null,
      "source": "import",
      "external_ref": null,
      "protected_fields": [],
      "project_id": "0199e3a0-0000-7000-8000-000000000101",
      "unit_model_id": "0199e3a0-0000-7000-8000-000000000201",
      "unit_number": "1203",
      "hold_expires_at": null,
      "created_by": null,
      "created_at": "2026-10-20T12:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z",
      "sale_price": {
        "amount": 3250.5,
        "currency": "UF",
        "amount_clp": 128500000,
        "amount_uf": 3250.5
      },
      "rent_price": null,
      "project": {
        "id": "0199e3a0-0000-7000-8000-000000000101",
        "name": "Parque Los Aromos"
      },
      "unit_model": {
        "id": "0199e3a0-0000-7000-8000-000000000201",
        "name": "3D2B 78 m²"
      }
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 100,
      "offset": 0
    },
    "status_counts": {
      "disponible": 1
    },
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

POST /units

Add a unidad by hand

A departamento, casa, estacionamiento or bodega of a proyecto; estacionamientos and bodegas are unidades of their own, priced and sold alone or with a departamento. The number is unique per proyecto and kind (409 otherwise); the tipología must be one of the proyecto’s. Born disponible. A unidad’s status is its negocio’s: by hand it can only be paused and put back (disponible ⇄ pausada), and no import ever writes it. Once the proyecto has a published lista de precios (/price-lists), its unidades’ prices change only by publishing a new version: a price here answers 409 unit_price_owned_by_price_list. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/units \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "kind": "estacionamiento",
    "unit_number": "E-12",
    "floor": -1,
    "sale_price_amount": 250,
    "sale_price_currency": "UF"
  }'

Example response (201)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000401",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "display_id": "I-42",
    "title": "Departamento 1203 · Parque Los Aromos",
    "description": null,
    "operation": "venta",
    "property_type": "departamento",
    "kind": "departamento",
    "status": "disponible",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "sector": null,
    "address": null,
    "sale_price_amount": 3250.5,
    "sale_price_currency": "UF",
    "rent_price_amount": null,
    "rent_price_currency": null,
    "common_expenses_clp": null,
    "property_tax_clp": null,
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "parking_spaces": null,
    "storage_units": null,
    "orientation": "nororiente",
    "floor": 12,
    "year_built": null,
    "furnished": null,
    "pets_allowed": null,
    "photos": [],
    "broker_user_id": null,
    "owner_name": null,
    "owner_phone": null,
    "owner_email": null,
    "source": "import",
    "external_ref": null,
    "protected_fields": [],
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "unit_model_id": "0199e3a0-0000-7000-8000-000000000201",
    "unit_number": "1203",
    "hold_expires_at": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sale_price": {
      "amount": 3250.5,
      "currency": "UF",
      "amount_clp": 128500000,
      "amount_uf": 3250.5
    },
    "rent_price": null,
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "unit_model": {
      "id": "0199e3a0-0000-7000-8000-000000000201",
      "name": "3D2B 78 m²"
    }
  }
}

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

DELETE /units/{id}

Remove a unidad

Takes it out of the stock. A unidad a negocio holds (bloqueada, reservada, promesada, vendida) cannot be removed (409 unit_held_by_negocio). Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1, máx. 64
curl -X DELETE https://api.vitrinadev.com/api/v1/units/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000401",
    "display_id": "I-42",
    "unit_number": "1203",
    "deleted": true
  }
}

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

GET /units/{id}

One unidad

By uuid or display id (I-42). Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1, máx. 64
curl https://api.vitrinadev.com/api/v1/units/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000401",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "display_id": "I-42",
    "title": "Departamento 1203 · Parque Los Aromos",
    "description": null,
    "operation": "venta",
    "property_type": "departamento",
    "kind": "departamento",
    "status": "disponible",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "sector": null,
    "address": null,
    "sale_price_amount": 3250.5,
    "sale_price_currency": "UF",
    "rent_price_amount": null,
    "rent_price_currency": null,
    "common_expenses_clp": null,
    "property_tax_clp": null,
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "parking_spaces": null,
    "storage_units": null,
    "orientation": "nororiente",
    "floor": 12,
    "year_built": null,
    "furnished": null,
    "pets_allowed": null,
    "photos": [],
    "broker_user_id": null,
    "owner_name": null,
    "owner_phone": null,
    "owner_email": null,
    "source": "import",
    "external_ref": null,
    "protected_fields": [],
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "unit_model_id": "0199e3a0-0000-7000-8000-000000000201",
    "unit_number": "1203",
    "hold_expires_at": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sale_price": {
      "amount": 3250.5,
      "currency": "UF",
      "amount_clp": 128500000,
      "amount_uf": 3250.5
    },
    "rent_price": null,
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "unit_model": {
      "id": "0199e3a0-0000-7000-8000-000000000201",
      "name": "3D2B 78 m²"
    }
  }
}

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

PATCH /units/{id}

Edit a unidad

Partial update; null clears a field. The proyecto is fixed. A field edited by hand on an imported unidad is protected from the next import (release it with POST /units/\{id\}/release-fields). A unidad’s status is its negocio’s: by hand it can only be paused and put back (disponible ⇄ pausada), and no import ever writes it. A status change while a negocio holds the unidad is a 409 unit_status_owned_by_negocio. Once the proyecto has a published lista de precios (/price-lists), its unidades’ prices change only by publishing a new version: a price here answers 409 unit_price_owned_by_price_list. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1, máx. 64
curl -X PATCH https://api.vitrinadev.com/api/v1/units/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "pausada"
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000401",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "display_id": "I-42",
    "title": "Departamento 1203 · Parque Los Aromos",
    "description": null,
    "operation": "venta",
    "property_type": "departamento",
    "kind": "departamento",
    "status": "disponible",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "sector": null,
    "address": null,
    "sale_price_amount": 3250.5,
    "sale_price_currency": "UF",
    "rent_price_amount": null,
    "rent_price_currency": null,
    "common_expenses_clp": null,
    "property_tax_clp": null,
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "parking_spaces": null,
    "storage_units": null,
    "orientation": "nororiente",
    "floor": 12,
    "year_built": null,
    "furnished": null,
    "pets_allowed": null,
    "photos": [],
    "broker_user_id": null,
    "owner_name": null,
    "owner_phone": null,
    "owner_email": null,
    "source": "import",
    "external_ref": null,
    "protected_fields": [],
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "unit_model_id": "0199e3a0-0000-7000-8000-000000000201",
    "unit_number": "1203",
    "hold_expires_at": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sale_price": {
      "amount": 3250.5,
      "currency": "UF",
      "amount_clp": 128500000,
      "amount_uf": 3250.5
    },
    "rent_price": null,
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "unit_model": {
      "id": "0199e3a0-0000-7000-8000-000000000201",
      "name": "3D2B 78 m²"
    }
  }
}

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

GET /units/{id}/price-history

A unidad’s price in every published lista de precios

Newest version first: the price in UF and in CLP with the day’s UF (meta.uf_value), who set and who published it, and when. Requires marketplace:read. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1, máx. 64
curl https://api.vitrinadev.com/api/v1/units/<id>/price-history \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "price_list_version_id": "0199e3a0-0000-7000-8000-000000000501",
      "version_number": 2,
      "name": "Lista noviembre 2026",
      "is_current": true,
      "published_at": "2026-10-20T12:30:00.000Z",
      "published_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "published_by_kind": "ui_member",
      "superseded_at": null,
      "price_uf": 3310,
      "price_clp": 130851317,
      "set_by": "0199e3a0-0000-7000-8000-0000000000bb",
      "set_by_kind": "ui_member",
      "set_at": "2026-10-20T12:10:00.000Z"
    }
  ],
  "meta": {
    "uf_value": {
      "clp": 39532.12,
      "date": "2026-10-20"
    }
  }
}

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

POST /units/{id}/release-fields

Let a unidad’s protected fields follow the import again

Removes fields from protected_fields, so the next import may overwrite them. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1, máx. 64

Body

FieldTypeRequiredConstraints
fieldsstring[]yes
curl -X POST https://api.vitrinadev.com/api/v1/units/<id>/release-fields \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      "sale_price_amount"
    ]
  }'

Example response (200)

{
  "data": {
    "id": "0199e3a0-0000-7000-8000-000000000401",
    "tenant_id": "0199e3a0-0000-7000-8000-0000000000aa",
    "display_id": "I-42",
    "title": "Departamento 1203 · Parque Los Aromos",
    "description": null,
    "operation": "venta",
    "property_type": "departamento",
    "kind": "departamento",
    "status": "disponible",
    "region": "Metropolitana",
    "comuna": "La Florida",
    "sector": null,
    "address": null,
    "sale_price_amount": 3250.5,
    "sale_price_currency": "UF",
    "rent_price_amount": null,
    "rent_price_currency": null,
    "common_expenses_clp": null,
    "property_tax_clp": null,
    "bedrooms": 3,
    "bathrooms": 2,
    "usable_area_m2": 72.5,
    "terrace_area_m2": 6,
    "total_area_m2": 78.5,
    "parking_spaces": null,
    "storage_units": null,
    "orientation": "nororiente",
    "floor": 12,
    "year_built": null,
    "furnished": null,
    "pets_allowed": null,
    "photos": [],
    "broker_user_id": null,
    "owner_name": null,
    "owner_phone": null,
    "owner_email": null,
    "source": "import",
    "external_ref": null,
    "protected_fields": [],
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "unit_model_id": "0199e3a0-0000-7000-8000-000000000201",
    "unit_number": "1203",
    "hold_expires_at": null,
    "created_by": null,
    "created_at": "2026-10-20T12:00:00.000Z",
    "updated_at": "2026-10-20T12:00:00.000Z",
    "sale_price": {
      "amount": 3250.5,
      "currency": "UF",
      "amount_clp": 128500000,
      "amount_uf": 3250.5
    },
    "rent_price": null,
    "project": {
      "id": "0199e3a0-0000-7000-8000-000000000101",
      "name": "Parque Los Aromos"
    },
    "unit_model": {
      "id": "0199e3a0-0000-7000-8000-000000000201",
      "name": "3D2B 78 m²"
    }
  }
}

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

POST /units/import

Import steps 2–3: dry run, then load a proyecto’s unidades

Loads a proyecto’s stock from the inmobiliaria’s Excel or CSV (multipart: file, project_id, mapping as JSON text, dry_run, default_kind) or from JSON rows (one object per unidad keyed by column name — the MCP path). dry_run defaults to true: the report says, row by row, what would be created, updated (which fields) or left unchanged, and every error with its field; nothing is written. Send dry_run: false to write every row without an error in ONE transaction. A unidad is matched by proyecto + kind + number, so re-importing a file changes nothing and an updated file updates in place; unidades missing from the file are counted (not_in_file), never removed. An import never writes a unidad’s status (there is no status field; a column like «Estado» is listed in ignored_status_columns), and fields a person edited by hand stay (protected_skipped). A tipología is named by its name in the proyecto; its dormitorios, baños and m² fill a new unidad’s when the file leaves them empty. Once the proyecto has a published lista de precios, the price column is left out (ignored_price_column): prices then come from the list only. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/units/import \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "rows": [
      {
        "Número": "1203",
        "Tipo": "Depto",
        "Tipología": "3D2B",
        "Piso": 12,
        "Precio UF": "3.250,5"
      },
      {
        "Número": "E-12",
        "Tipo": "Estacionamiento",
        "Piso": -1,
        "Precio UF": 250
      }
    ],
    "mapping": {
      "unit_number": "Número",
      "kind": "Tipo",
      "unit_model": "Tipología",
      "floor": "Piso",
      "price_uf": "Precio UF"
    },
    "dry_run": true
  }'

Example response (200)

{
  "data": {
    "dry_run": true,
    "project_id": "0199e3a0-0000-7000-8000-000000000101",
    "mapping": {
      "unit_number": "Número",
      "kind": "Tipo",
      "unit_model": "Tipología",
      "floor": "Piso",
      "price_uf": "Precio UF"
    },
    "ignored_status_columns": [
      "Estado"
    ],
    "ignored_price_column": null,
    "summary": {
      "total_rows": 3,
      "create": 1,
      "update": 1,
      "unchanged": 0,
      "error": 1,
      "not_in_file": 0
    },
    "rows": [
      {
        "row_number": 2,
        "action": "create",
        "unit_number": "1203",
        "kind": "departamento",
        "unit_id": null,
        "display_id": null,
        "fields": [
          "floor",
          "sale_price_amount",
          "sale_price_currency",
          "unit_model_id"
        ],
        "protected_skipped": [],
        "status": "disponible",
        "errors": []
      },
      {
        "row_number": 3,
        "action": "update",
        "unit_number": "E-12",
        "kind": "estacionamiento",
        "unit_id": "0199e3a0-0000-7000-8000-000000000401",
        "display_id": "I-43",
        "fields": [
          "sale_price_amount"
        ],
        "protected_skipped": [],
        "status": "reservada",
        "errors": []
      },
      {
        "row_number": 4,
        "action": "error",
        "unit_number": "1204",
        "kind": "departamento",
        "unit_id": null,
        "display_id": null,
        "fields": [],
        "protected_skipped": [],
        "status": "disponible",
        "errors": [
          {
            "field": "unit_model",
            "message": "La tipología «4D3B» no existe en este proyecto. Créala primero o corrige el nombre."
          }
        ]
      }
    ]
  }
}

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

POST /units/import/columns

Import step 1: read a stock file’s columns

Multipart, the file in a part named file (.xlsx, .xls or .csv, up to 20 MB; the first sheet with data, the first non-empty row is the header). Returns the header, the first rows, the mapping we suggest from the column names and every field a column can feed — the mapping screen. Writes and stores nothing: send the same file to POST /units/import. Requires marketplace:write. Only on a workspace that runs the Proyectos module (real-estate vertical, inmobiliaria); any other answers 403 MODULE_NOT_ENABLED.

curl -X POST https://api.vitrinadev.com/api/v1/units/import/columns \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -F "file=@/path/to/file.png"

Example response (200)

{
  "data": {
    "format": "xlsx",
    "sheet_name": "Stock",
    "header": [
      "Número",
      "Tipo",
      "Tipología",
      "Piso",
      "Precio UF",
      "Estado"
    ],
    "row_count": 200,
    "sample": [
      [
        "1203",
        "Depto",
        "3D2B",
        "12",
        "3.250,5",
        "Disponible"
      ]
    ],
    "suggested_mapping": {
      "unit_number": "Número",
      "kind": "Tipo",
      "unit_model": "Tipología",
      "floor": "Piso",
      "price_uf": "Precio UF"
    },
    "ignored_status_columns": [
      "Estado"
    ],
    "fields": [
      {
        "key": "unit_number",
        "label": "Número de unidad",
        "required": true,
        "description": "The unidad’s number as the inmobiliaria writes it."
      }
    ]
  }
}

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

On this page

DELETE /case-assignments/{id}POST /case-assignments/{id}/linkGET /channel-commissionsPOST /channel-commissions/{id}/cancelPOST /channel-commissions/{id}/payPOST /channel-commissions/milestonesGET /channel-partnersPOST /channel-partnersGET /channel-partners/{id}PATCH /channel-partners/{id}GET /channel-partners/{id}/claimsPOST /channel-partners/{id}/claimsGET /cobranzaGET /contratistasPOST /contratistasDELETE /contratistas/{id}GET /contratistas/{id}PATCH /contratistas/{id}GET /cotizacion-discount-approvalsPOST /cotizacion-discount-approvals/{id}/decisionGET /cotizacionesPOST /cotizacionesDELETE /cotizaciones/{id}GET /cotizaciones/{id}PATCH /cotizaciones/{id}GET /cotizaciones/{id}/discount-approvalPOST /cotizaciones/{id}/discount-approvalGET /cotizaciones/{id}/pdfPOST /cotizaciones/{id}/reservaPOST /cotizaciones/{id}/sendGET /cotizaciones/{id}/viewsPOST /cotizaciones/calculatePOST /datahunter-importGET /datahunter-import/batchesPOST /datahunter-import/columnsDELETE /floor-plans/{id}GET /floor-plans/{id}PATCH /floor-plans/{id}PUT /floor-plans/{id}/unitsGET /handoversPOST /handoversDELETE /handovers/{id}GET /handovers/{id}PATCH /handovers/{id}POST /handovers/{id}/attestPOST /handovers/{id}/casosPATCH /handovers/{id}/items/{itemId}POST /handovers/{id}/mediaGET /handovers/{id}/pdfPOST /handovers/{id}/reopenPOST /handovers/{id}/sendGET /negociosGET /negocios/{id}GET /negocios/{id}/bank-approvalsPOST /negocios/{id}/bank-approvalsDELETE /negocios/{id}/bank-approvals/{approvalId}PATCH /negocios/{id}/bank-approvals/{approvalId}GET /negocios/{id}/buyer-viewGET /negocios/{id}/cuotas/{number}/paymentPATCH /negocios/{id}/deadlinesPOST /negocios/{id}/desistimientoGET /negocios/{id}/documentsGET /negocios/{id}/documents/{documentId}/filePOST /negocios/{id}/documents/{documentId}/filePOST /negocios/{id}/documents/{documentId}/reviewPOST /negocios/{id}/entregaPOST /negocios/{id}/escrituraPOST /negocios/{id}/pie-planPOST /negocios/{id}/promesaGET /negocios/{id}/signaturesPOST /negocios/{id}/signaturesPOST /negocios/{id}/signatures/{signatureId}/cancelGET /negocios/{id}/signatures/{signatureId}/files/{file}POST /negocios/{id}/signatures/{signatureId}/resendGET /negocios/{id}/statementGET /negocios/{id}/statement/pdfPOST /negocios/{id}/statement/sendGET /postventa/reportGET /postventa/unit-ownersPUT /postventa/unit-ownersDELETE /postventa/unit-owners/{id}GET /price-listsPOST /price-listsDELETE /price-lists/{id}GET /price-lists/{id}PATCH /price-lists/{id}PATCH /price-lists/{id}/entriesPOST /price-lists/{id}/importPOST /price-lists/{id}/publishPOST /price-lists/import/columnsGET /project-listingsPOST /project-listingsPATCH /project-listings/{id}GET /projectsPOST /projectsDELETE /projects/{id}GET /projects/{id}PATCH /projects/{id}GET /projects/{id}/document-requirementsPUT /projects/{id}/document-requirementsDELETE /projects/{id}/entrega-checklistGET /projects/{id}/entrega-checklistPUT /projects/{id}/entrega-checklistGET /projects/{id}/floor-plansPOST /projects/{id}/floor-plansGET /projects/{id}/marketing-kitPATCH /projects/{id}/marketing-kitGET /projects/{id}/marketing-kit/audiencePOST /projects/{id}/marketing-kit/broadcastPOST /projects/{id}/marketing-kit/generateGET /projects/{id}/membersPOST /projects/{id}/membersDELETE /projects/{id}/members/{userId}GET /projects/{id}/milestone-termsPUT /projects/{id}/milestone-termsGET /projects/{id}/obra-updatesPOST /projects/{id}/obra-updatesDELETE /projects/{id}/obra-updates/{updateId}POST /projects/{id}/obra-updates/{updateId}/announcePOST /projects/{id}/obra-updates/{updateId}/photosDELETE /projects/{id}/obra-updates/{updateId}/photos/{index}GET /projects/{id}/obra-updates/{updateId}/photos/{index}GET /projects/{id}/pie-termsPUT /projects/{id}/pie-termsGET /projects/{id}/publication-readinessGET /projects/{id}/publicationsPOST /projects/{id}/publicationsDELETE /projects/{id}/publications/{publicationId}GET /projects/{id}/reserva-termsPUT /projects/{id}/reserva-termsDELETE /projects/{id}/signing-templates/{kind}GET /projects/{id}/signing-templates/{kind}PUT /projects/{id}/signing-templates/{kind}GET /projects/{id}/toctocPUT /projects/{id}/toctocGET /projects/{id}/whatsapp-linksGET /real-estate-offersGET /real-estate-offers/settingsPUT /real-estate-offers/settingsGET /real-estate-reports/projectsGET /reservasGET /reservas/{id}POST /reservas/{id}/cancelGET /reservas/{id}/paymentGET /toctoc-connectionPOST /toctoc-connection/tokenGET /toctoc-listingsPOST /toctoc-listingsPATCH /toctoc-listings/{id}GET /unit-modelsPOST /unit-modelsDELETE /unit-models/{id}GET /unit-models/{id}PATCH /unit-models/{id}GET /unitsPOST /unitsDELETE /units/{id}GET /units/{id}PATCH /units/{id}GET /units/{id}/price-historyPOST /units/{id}/release-fieldsPOST /units/importPOST /units/import/columns