Sell a unit
The documents a dealership quotes, reserves and sells with.
Car dealershipsOnly in car-dealership workspaces.
POST /quotes issues a quote and allocates its folio. POST /reservations holds the unit. POST /sale-notes sells it, and the car turns vendido once somebody approves that note.
None of the nine resources below is freely editable. Each takes a short set of verbs. Voiding keeps the folio and the reason on the document; to correct one, void it and issue another.
| Resource | Scope | What it is |
|---|---|---|
| Publications | marketplace:read / marketplace:write | The car's state on each connected portal. |
| Quotes | quotes:read / quotes:write / quotes:void | A frozen price offer. It holds nothing. |
| Reservations | reservations:read / reservations:write / reservations:void / reservations:dispose_abono | A hold on one unit, with an optional deposit. |
| Sale notes | sale_notes:read / sale_notes:write / sale_notes:void | The document that sells the car. |
| Purchase notes | purchase_notes:read / purchase_notes:write / purchase_notes:void | The document that gives an incoming car its cost. |
| Document payments | document_payments:read / document_payments:write | Money against a reservation or a sale note. |
| Consignments | consignments:read / consignments:write | The contract for selling somebody else's car. |
| Credit applications | credit_applications:read / credit_applications:write | The file that chases a bank. |
| Price approvals | price_approval:read / price_approval:request / price_approval:approve | Authority over a below-reference price. |
All of these POSTs take Idempotency-Key. Repeat the call with the same value and you get the document you already issued, with no second folio spent. Errors lists the codes.
The responses on this page come from a run against a test workspace. Your identifiers will differ; the shape will not.
Publish to portals
curl -X POST https://api.vitrinadev.com/api/v1/vehicles/de7d959a-292f-4491-92ce-2028fa9114f0/publish \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{ "integration_ids": ["e6038419-209e-42e2-80ed-2a8451156a8a"] }'integration_ids names the portals to publish to. The response carries one verdict per integration, so a 200 doesn't yet mean the ad is live:
{
"data": [
{
"integration_id": "e6038419-209e-42e2-80ed-2a8451156a8a",
"provider": "facebook_marketplace",
"status": "pending",
"external_id": "fbm-task:b802ddef-cf01-41e6-9072-2f571c06acfc",
"publication": {
"id": "aed50dde-d45b-47f2-9564-fdcb296b14f3",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"marketplace_account_id": "e6038419-209e-42e2-80ed-2a8451156a8a",
"provider": "facebook_marketplace",
"external_id": "fbm-task:b802ddef-cf01-41e6-9072-2f571c06acfc",
"status": "pending",
"origin": "vitrina",
"permalink": null,
"last_synced_at": null,
"last_published_at": "2026-09-23T01:28:12.731Z",
"last_error": null,
"close_retry_count": 0,
"portal_state": null,
"portal_state_at": null,
"raw_provider_payload": {
"taskId": "b802ddef-cf01-41e6-9072-2f571c06acfc"
},
"created_at": "2026-09-23T01:28:12.743Z",
"updated_at": "2026-09-23T01:28:12.743Z"
}
}
]
}Trap
Facebook Marketplace answers pending because no ad exists yet
The Vitrina extension in a salesperson's browser completes the publication, at
its own pace. That's why external_id is a provisional handle (fbm-task:…)
and permalink comes back null. If the extension is off or paused, the
publication doesn't move. The real outcome arrives through
GET /vehicles/{id}/publications or through the events further down.
DELETE /vehicles/{id}/publications/{pubId} withdraws one publication, and POST /vehicles/{id}/close-ads closes every ad on a car. The row itself never disappears. It keeps the portal's terminal state, so "what happened to that publication?" always has an answer.
{
"data": {
"id": "aed50dde-d45b-47f2-9564-fdcb296b14f3",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"marketplace_account_id": "e6038419-209e-42e2-80ed-2a8451156a8a",
"provider": "facebook_marketplace",
"external_id": "fbm-task:b802ddef-cf01-41e6-9072-2f571c06acfc",
"status": "removing",
"origin": "vitrina",
"permalink": null,
"last_synced_at": null,
"last_published_at": "2026-09-23T01:28:12.731Z",
"last_error": null,
"close_retry_count": 0,
"portal_state": null,
"portal_state_at": null,
"raw_provider_payload": {
"taskId": "b802ddef-cf01-41e6-9072-2f571c06acfc"
},
"created_at": "2026-09-23T01:28:12.743Z",
"updated_at": "2026-09-23T01:28:22.295Z"
}
}Quote
curl -X POST https://api.vitrinadev.com/api/v1/quotes \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"offered_price_clp": 8990000,
"tax_treatment": "afecto",
"expires_on": "2026-09-30"
}'{
"data": {
"id": "982dd531-6b1a-49f1-a947-f8520e7bb648",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "Q-7",
"display_seq": 7,
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"salesperson_id": null,
"list_price_clp": 8990000,
"offered_price_clp": 8990000,
"tax_treatment": "afecto",
"discount_pct": 0,
"unit_make": "Peugeot",
"unit_model": "208",
"unit_version": "Active 1.2",
"unit_year": 2021,
"unit_plate": "KXPW34",
"unit_vin": "VF3CCHMZ6MT012345",
"unit_odometer_value": 38200,
"unit_odometer_unit": "KM",
"unit_color": "Gris",
"discount_hidden": false,
"issued_at": "2026-09-23T01:24:47.318Z",
"expires_on": "2026-09-30",
"state": "vigente",
"voided_at": null,
"void_reason": null,
"voided_by": null,
"extended_at": null,
"extend_reason": null,
"extended_by": null,
"archived_at": null,
"archive_reason": null,
"archived_by": null,
"created_at": "2026-09-23T01:24:47.318Z",
"updated_at": "2026-09-23T01:24:47.318Z"
}
}The unit_* fields are the car's identity frozen at issue: make, model, version, year, plate, VIN, odometer and colour. Repricing or correcting the car later changes no quote already issued, which is why those fields aren't request parameters either.
The folio Q-7 works anywhere a route takes {id}. state is derived (vigente, expirada or nula), and a quote stays vigente for the whole of expires_on's day.
An issued document takes exactly three changes: POST /quotes/{id}/void, POST /quotes/{id}/extend and POST /quotes/{id}/archive. All three want a visible void_reason, extend_reason or archive_reason. None takes the actor from the body: voided_by is the authenticated principal.
Trap
A nula quote can't be voided twice
The second void is a 409, and the first actor and reason stand. To
correct it, issue another quote.
{
"error": {
"code": "CONFLICT",
"message": "Quote Q-8 is already voided; a void cannot be restated.",
"requestId": "8a40a45a-975d-4fa4-a685-a40deabe4c6e"
}
}Reserve
A reservation is the only document that holds the unit. Several customers can hold live quotes on the same car; only one reservation can be activa.
curl -X POST https://api.vitrinadev.com/api/v1/reservations \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicle_id": "81e56530-5365-4e4c-a423-2bb043798527",
"holder_contact_id": "01a0cbdd-305f-7312-9345-5818a81e1fd8",
"agreed_price_clp": 10490000,
"hold_expires_on": "2026-09-30"
}'{
"data": {
"id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "R-5",
"display_seq": 5,
"vehicle_id": "81e56530-5365-4e4c-a423-2bb043798527",
"holder_contact_id": "01a0cbdd-305f-7312-9345-5818a81e1fd8",
"salesperson_id": null,
"agreed_price_clp": 10490000,
"abono_expected_clp": 300000,
"taken_at": "2026-09-23T01:25:07.880Z",
"hold_expires_on": "2026-09-30",
"keep_advertised": true,
"status": "activa",
"abono_disposition": null,
"abono_disposition_reason": null,
"abono_disposition_at": null,
"abono_disposition_by": null,
"voided_at": null,
"void_reason": null,
"voided_by": null,
"created_at": "2026-09-23T01:25:07.880Z",
"updated_at": "2026-09-23T01:25:07.880Z"
}
}hold_expires_on is required and has no default. A hold locks inventory and can take a non-refundable deposit, so each one states its own term. Leave agreed_price_clp out and the car's current asking price is copied in. keep_advertised decides whether the portal ads stay live while the hold lasts.
Closing a hold with payments on it means saying where the money went. The route is POST /reservations/{id}/abono-disposition, the fate is aplicado, devuelto or perdido, and the same call wants the void_reason because it closes the hold:
{
"data": {
"id": "96b5d519-20af-4afb-9d85-d1c5d1134fa6",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "R-6",
"display_seq": 6,
"vehicle_id": "c9309769-a5d0-4ec3-a72b-c38463df3bf7",
"holder_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"salesperson_id": null,
"agreed_price_clp": 11290000,
"abono_expected_clp": 300000,
"taken_at": "2026-09-23T01:28:50.056Z",
"hold_expires_on": "2026-09-30",
"keep_advertised": false,
"status": "anulada",
"abono_disposition": "perdido",
"abono_disposition_reason": "Abono no reembolsable según el contrato firmado",
"abono_disposition_at": "2026-09-23T01:29:11.554Z",
"abono_disposition_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"voided_at": "2026-09-23T01:29:11.554Z",
"void_reason": "El comprador no volvió",
"voided_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"created_at": "2026-09-23T01:28:50.056Z",
"updated_at": "2026-09-23T01:29:11.554Z"
}
}POST /reservations/{id}/convert produces a fresh sale note, inherits the car and the buyer, and carries the payments over to the new document. The price comes from agreed_price_clp and isn't a parameter of this route.
{
"data": {
"reservation": {
"id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "R-5",
"display_seq": 5,
"vehicle_id": "81e56530-5365-4e4c-a423-2bb043798527",
"holder_contact_id": "01a0cbdd-305f-7312-9345-5818a81e1fd8",
"salesperson_id": null,
"agreed_price_clp": 10490000,
"abono_expected_clp": 300000,
"taken_at": "2026-09-23T01:25:07.880Z",
"hold_expires_on": "2026-09-30",
"keep_advertised": true,
"status": "completada",
"abono_disposition": "aplicado",
"abono_disposition_reason": "Abono aplicado a la nota de venta V-2 (BR-237): la reserva R-5 se completó con la venta y el dinero recibido se acreditó en el documento resultante, copiado pago por pago (BR-291).",
"abono_disposition_at": "2026-09-23T01:25:24.288Z",
"abono_disposition_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"voided_at": null,
"void_reason": null,
"voided_by": null,
"created_at": "2026-09-23T01:25:07.880Z",
"updated_at": "2026-09-23T01:25:24.242Z"
},
"sale_note": {
"id": "32e4f73b-1b37-407a-9442-0c110cd68d08",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "V-2",
"display_seq": 2,
"vehicle_id": "81e56530-5365-4e4c-a423-2bb043798527",
"buyer_contact_id": "01a0cbdd-305f-7312-9345-5818a81e1fd8",
"lead_id": null,
"converted_from_reservation_id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"seller_of_record": "automotora",
"salesperson_id": null,
"net_clp": 10490000,
"tax_clp": null,
"tax_treatment": "afecto",
"status": "issued",
"issued_at": "2026-09-23T01:25:24.242Z",
"approved_by": null,
"approved_at": null,
"voided_at": null,
"void_reason": null,
"voided_by": null,
"created_at": "2026-09-23T01:25:24.242Z",
"updated_at": "2026-09-23T01:25:24.242Z"
},
"carried_payments": [
{
"id": "01a0cbde-13ff-7fba-bc54-a0b8b687bcf1",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"reservation_id": null,
"sale_note_id": "32e4f73b-1b37-407a-9442-0c110cd68d08",
"carried_from_reservation_id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"instrument": "transferencia",
"amount_clp": 300000,
"paid_on": "2026-09-23",
"bank": "Banco de Chile",
"account_number": null,
"document_number": "8842190",
"note": null,
"card_fee_bps": null,
"card_surcharge_clp": null,
"card_surcharge_source": null,
"created_at": "2026-09-23T01:25:24.095Z",
"updated_at": "2026-09-23T01:25:24.174Z"
}
]
}
}Sell
curl -X POST https://api.vitrinadev.com/api/v1/sale-notes \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"seller_of_record": "automotora",
"net_clp": 8990000,
"tax_clp": 1708100,
"tax_treatment": "afecto"
}'Issuing allocates the folio and leaves the note issued. The sale closes with POST /sale-notes/{id}/approve:
{
"data": {
"id": "486de281-2d4a-4e0c-a98c-95617e1b4093",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "V-4",
"display_seq": 4,
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"lead_id": null,
"converted_from_reservation_id": null,
"seller_of_record": "automotora",
"salesperson_id": null,
"net_clp": 8990000,
"tax_clp": 1708100,
"tax_treatment": "afecto",
"status": "approved",
"issued_at": "2026-09-23T01:34:49.722Z",
"approved_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"approved_at": "2026-09-23T01:34:51.127Z",
"voided_at": null,
"void_reason": null,
"voided_by": null,
"created_at": "2026-09-23T01:34:49.722Z",
"updated_at": "2026-09-23T01:34:51.125Z"
}
}Approving marks the car vendido, a terminal state, and in the same act voids every quote still live on that unit:
{
"data": {
"id": "982dd531-6b1a-49f1-a947-f8520e7bb648",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "Q-7",
"display_seq": 7,
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"salesperson_id": null,
"list_price_clp": 8990000,
"offered_price_clp": 8990000,
"tax_treatment": "afecto",
"discount_pct": 0,
"unit_make": "Peugeot",
"unit_model": "208",
"unit_version": "Active 1.2",
"unit_year": 2021,
"unit_plate": "KXPW34",
"unit_vin": "VF3CCHMZ6MT012345",
"unit_odometer_value": 38200,
"unit_odometer_unit": "KM",
"unit_color": "Gris",
"discount_hidden": false,
"issued_at": "2026-09-23T01:24:47.318Z",
"expires_on": "2026-09-30",
"state": "nula",
"voided_at": "2026-09-23T01:34:51.156Z",
"void_reason": "Vehículo vendido (BR-215): la validez de una cotización vigente termina en el momento en que se vende la unidad, sin esperar a su fecha de expiración impresa — la validez es la que ocurra primero entre ambas.",
"voided_by": "00000000-0000-0000-0000-000000000000",
"extended_at": null,
"extend_reason": null,
"extended_by": null,
"archived_at": null,
"archive_reason": null,
"archived_by": null,
"created_at": "2026-09-23T01:24:47.318Z",
"updated_at": "2026-09-23T01:34:51.125Z"
}
}Trap
A car takes one live sale note at a time
Issuing a second one against the same unit is a 409, and no folio is spent.
{
"error": {
"code": "CONFLICT",
"message": "Ya existe una nota de venta vigente (V-5) para este vehículo. Anúlala antes de emitir otra: un vehículo se vende una sola vez, y dos notas de venta vigentes sobre la misma unidad significan dos ventas del mismo auto. No se escribió nada y no se consumió ningún folio.",
"requestId": "e41f51cc-48c5-40b8-8d1d-5c185b4ec6c5"
}
}When net_clp falls below the level this organisation requires authorisation for, approve answers 409. It wants an approved price approval naming that same document, which Authorise a discount shows how to raise.
GET /sale-notes/{id} adds totals and funding, the verdict on whether the money committed covers the price. With the figures complete and nothing paid yet, standing reads short; with tax_clp missing it reads not_computable, and gaps says what is missing. Voiding wants sale_notes:void, a different authority from sale_notes:write.
Buy
curl -X POST https://api.vitrinadev.com/api/v1/purchase-notes \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"seller_contact_id": "01a0cbdd-307a-75af-8566-1f9b09686cb8",
"net_clp": 6200000,
"tax_treatment": "no_gravado",
"lines": [
{ "description": "Compra de vehículo usado Mazda CX-5 2020", "net_clp": 6200000, "tax_treatment": "no_gravado" }
]
}'{
"data": {
"id": "6e84a7d8-ccc0-4ed5-b03e-ec81613308e5",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "P-3",
"display_seq": 3,
"seller_contact_id": "01a0cbdd-307a-75af-8566-1f9b09686cb8",
"net_clp": 6200000,
"tax_clp": null,
"tax_treatment": "no_gravado",
"status": "issued",
"issued_at": "2026-09-23T01:34:18.007Z",
"corrected_at": null,
"corrected_by": null,
"voided_at": null,
"void_reason": null,
"voided_by": null,
"created_at": "2026-09-23T01:34:18.007Z",
"updated_at": "2026-09-23T01:34:18.007Z",
"lines": [
{
"id": "e4be40c0-0f87-40be-8698-15feb843bbef",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"purchase_note_id": "6e84a7d8-ccc0-4ed5-b03e-ec81613308e5",
"sale_note_id": null,
"position": 1,
"description": "Compra de vehículo usado Mazda CX-5 2020",
"net_clp": 6200000,
"tax_clp": null,
"tax_treatment": "no_gravado",
"add_on_kind": null,
"add_on_framing": null,
"cost_clp": null,
"beneficiary": null,
"documents_in_customer_name": null,
"fee_kind": null,
"fee_effective_from": null,
"created_at": "2026-09-23T01:34:18.007Z",
"updated_at": "2026-09-23T01:34:18.007Z"
}
],
"acquisition": null,
"retentions": []
}
}A purchase note gives an incoming car its cost. Every line carries its own net_clp and its own tax_treatment. The link to the unit is recorded separately, at /vehicle-acquisitions.
PATCH /purchase-notes/{id} corrects a live document without touching its folio or its status, and leaves corrected_at and corrected_by behind. Sending lines replaces the whole breakdown.
Pay
curl -X POST https://api.vitrinadev.com/api/v1/document-payments \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reservation_id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"instrument": "transferencia",
"amount_clp": 300000,
"paid_on": "2026-09-23",
"bank": "Banco de Chile",
"document_number": "8842190"
}'{
"data": {
"id": "01a0cbde-13ff-7fba-bc54-a0b8b687bcf1",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"reservation_id": "55923d26-8109-4d13-85aa-8ae2b9b10454",
"sale_note_id": null,
"carried_from_reservation_id": null,
"instrument": "transferencia",
"amount_clp": 300000,
"paid_on": "2026-09-23",
"bank": "Banco de Chile",
"account_number": null,
"document_number": "8842190",
"note": null,
"card_fee_bps": null,
"card_surcharge_clp": null,
"card_surcharge_source": null,
"created_at": "2026-09-23T01:25:24.095Z",
"updated_at": "2026-09-23T01:25:24.095Z"
}
}Trap
A transferencia with no bank is a 400
The instrument decides which fields become required. Without the bank, nobody can match a line on a statement to the document it paid.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "A transferencia payment requires bank (BR-261/BR-447): these are the fields that let the cashier match a bank statement line to a document, and without them a returned instrument cannot be traced back to the sale it paid for.",
"requestId": "eb129b92-f686-4f4c-b62c-6cf19e6e57fa"
}
}This route stays for compatibility. For anything new the route is POST /payments: it splits one payment across several obligations, leaves the surplus as the contact's credit, and can be reversed.
Consign
Under a consignment, the dealership sells somebody else's car. Only vehicle_id and modalidad are required.
curl -X POST https://api.vitrinadev.com/api/v1/consignments \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicle_id": "bd4490ca-587e-4396-b0dc-074b4997b8a1",
"modalidad": "en_local"
}'{
"data": {
"id": "1357177d-cad8-49fa-8042-51ce1b1c4452",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"vehicle_id": "bd4490ca-587e-4396-b0dc-074b4997b8a1",
"dueno_contact_id": null,
"modalidad": "en_local",
"contract_structure": "mandato_con_representacion",
"comision_type": null,
"comision_value": null,
"minimo_clp": null,
"vencimiento": null,
"estado": "activo",
"vencimiento_reminded_at": null,
"liquidacion_due_notified_at": null,
"created_at": "2026-09-23T01:25:50.254Z",
"updated_at": "2026-09-23T01:25:50.254Z",
"sale_iva_regime": null,
"not_on_lot": false
}
}Creating the contract moves the car's tenencia to consignacion straight away. The owner, the commission and the floor are filled in later with PATCH /consignments/{id}. Until the commission is there, the contract can't be sold either:
{
"error": {
"code": "CONFLICT",
"message": "Este contrato no tiene comisión registrada, así que no hay reparto que liquidar. Registra comision_type y comision_value en el contrato antes de venderlo.",
"requestId": "7197732b-6386-47e9-9f84-fd1d12b3ac67"
}
}POST /consignments/{id}/sell moves the contract to vendido and produces the settlement in the same act, commission already deducted:
{
"data": {
"contract": {
"id": "1357177d-cad8-49fa-8042-51ce1b1c4452",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"vehicle_id": "bd4490ca-587e-4396-b0dc-074b4997b8a1",
"dueno_contact_id": "01a0cbdd-307a-75af-8566-1f9b09686cb8",
"modalidad": "en_local",
"contract_structure": "mandato_con_representacion",
"comision_type": "percentage",
"comision_value": 10,
"minimo_clp": 12000000,
"vencimiento": null,
"estado": "vendido",
"vencimiento_reminded_at": null,
"liquidacion_due_notified_at": null,
"created_at": "2026-09-23T01:25:50.254Z",
"updated_at": "2026-09-23T01:26:12.646Z",
"sale_iva_regime": null,
"not_on_lot": false
},
"liquidacion": {
"id": "765ad258-039c-43e6-b399-36b2e49ac281",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"consignment_contract_id": "1357177d-cad8-49fa-8042-51ce1b1c4452",
"vehicle_id": "bd4490ca-587e-4396-b0dc-074b4997b8a1",
"settlement_mode": "stated_commission",
"amount_venta_clp": 13890000,
"comision_type": "percentage",
"comision_value": 10,
"comision_amount_clp": 1389000,
"monto_owner_clp": 12501000,
"owner_floor_clp": null,
"deducciones_clp": 0,
"retiro_motivo": null,
"retiro_by": null,
"paid_at": "2026-09-23T01:26:12.648Z",
"created_at": "2026-09-23T01:26:12.646Z",
"updated_at": "2026-09-23T01:26:12.646Z"
}
}
}POST /consignments/{id}/return hands the car back to its owner and tenencia returns to propio. It's the only terminal state that restores it: a sold or expired contract keeps consignacion.
{
"data": {
"id": "42331a67-d7a0-45c2-8194-896a538edc42",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"vehicle_id": "a4fb12d7-ef13-4871-b77e-0b80cda38303",
"dueno_contact_id": "01a0cbdd-307a-75af-8566-1f9b09686cb8",
"modalidad": "en_local",
"contract_structure": "mandato_con_representacion",
"comision_type": "percentage",
"comision_value": 8,
"minimo_clp": 7000000,
"vencimiento": null,
"estado": "devuelto",
"vencimiento_reminded_at": null,
"liquidacion_due_notified_at": null,
"created_at": "2026-09-23T01:28:40.901Z",
"updated_at": "2026-09-23T01:28:40.972Z",
"sale_iva_regime": null,
"not_on_lot": false
}
}Finance
curl -X POST https://api.vitrinadev.com/api/v1/credit-applications \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"institution_id": "31512710-2c11-4ae2-b396-a394d7b82c16",
"requested_amount_clp": 6500000,
"term_months": 48
}'{
"data": {
"id": "09071d3d-e586-4415-ab98-03ef0bc681b3",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "F-3",
"display_seq": 3,
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"institution_id": "31512710-2c11-4ae2-b396-a394d7b82c16",
"requested_amount_clp": 6500000,
"vehicle_price_clp": null,
"declared_income_clp": null,
"fee_clp": null,
"term_months": 48,
"financed_total_clp": null,
"submitted_at": null,
"submitted_by": null,
"decided_at": null,
"decided_by": null,
"outcome": null,
"decision_reason": null,
"approved_amount_clp": null,
"withdrawn_at": null,
"withdrawn_by": null,
"withdrawn_reason": null,
"last_contact_at": null,
"last_contact_by": null,
"chased_at": null,
"state": "recorded",
"created_at": "2026-09-23T01:26:19.780Z",
"updated_at": "2026-09-23T01:26:19.780Z",
"taken": false,
"taken_sale_note_id": null,
"taken_sale_note_folio": null,
"institution_name": "Banco Falabella",
"taken_credit_state": null,
"taken_disbursed_at": null,
"vehicle_label": "Peugeot 208 2021",
"vehicle_plate": "KXPW34",
"buyer_name": "Daniela Ortiz",
"buyer_rut": null
}
}The file moves through facts, each on its own route and none of them a status edit. POST .../submission records that the paperwork went to the bank, with whatever submitted_at you pass. POST .../decision stores the lender's answer, and outcome takes approved or rejected. POST .../withdrawal pulls the file and is terminal. POST .../contact resets the chase clock and is the only repeatable one.
{
"data": {
"id": "09071d3d-e586-4415-ab98-03ef0bc681b3",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"display_id": "F-3",
"display_seq": 3,
"vehicle_id": "de7d959a-292f-4491-92ce-2028fa9114f0",
"buyer_contact_id": "01a0cbdd-304a-70d8-951f-5e4b481301c9",
"institution_id": "31512710-2c11-4ae2-b396-a394d7b82c16",
"requested_amount_clp": 6500000,
"vehicle_price_clp": null,
"declared_income_clp": null,
"fee_clp": null,
"term_months": 48,
"financed_total_clp": null,
"submitted_at": "2026-09-23T14:05:00.000Z",
"submitted_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"decided_at": "2026-09-23T01:26:38.761Z",
"decided_by": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"outcome": "approved",
"decision_reason": null,
"approved_amount_clp": 6000000,
"withdrawn_at": null,
"withdrawn_by": null,
"withdrawn_reason": null,
"last_contact_at": null,
"last_contact_by": null,
"chased_at": null,
"state": "approved",
"created_at": "2026-09-23T01:26:19.780Z",
"updated_at": "2026-09-23T01:26:38.760Z",
"taken": false,
"taken_sale_note_id": null,
"taken_sale_note_folio": null,
"institution_name": "Banco Falabella",
"taken_credit_state": null,
"taken_disbursed_at": null,
"vehicle_label": "Peugeot 208 2021",
"vehicle_plate": "KXPW34",
"buyer_name": "Daniela Ortiz",
"buyer_rut": null
}
}GET /credit-applications/ageing is the chase board, with each lender's own clock already worked out. It takes no threshold parameter: the term lives on the institution catalogue, and every row brings its own in sla_days.
Authorise a discount
curl -X POST https://api.vitrinadev.com/api/v1/price-approvals \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"sale_note_id": "81d9aba3-3901-4ba6-963b-a176f20ae99b",
"requested_price_clp": 7900000
}'{
"data": {
"id": "d0aace9b-bb03-40b1-9c09-1c2c7a08a2ac",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"quote_id": null,
"reservation_id": null,
"sale_note_id": "81d9aba3-3901-4ba6-963b-a176f20ae99b",
"requested_price_clp": 7900000,
"list_price_snapshot_clp": 8990000,
"floor_price_snapshot_clp": 8200000,
"requester_user_id": "0eb8e217-8a6f-4cb8-8216-b077194b3df7",
"status": "pending",
"decider_user_id": null,
"decided_at": null,
"decision_note": null,
"created_at": "2026-09-23T01:26:52.892Z",
"updated_at": "2026-09-23T01:26:52.892Z"
}
}A request names exactly one of quote_id, reservation_id or sale_note_id, and stores the list price and the floor as they stood. Asking for an authorisation nobody needs is a 400:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "10490000 needs no authorisation on V-2: it is at or above the reference this organisation approves against (tolerance 0 CLP). Sell it. An approval queue full of requests that needed no decision is a queue nobody reads.",
"requestId": "8ec43ba3-0153-4697-9254-bc29ddd601b5"
}
}Trap
The asker and the decider have to be different people
POST /price-approvals/{id}/decision answers 403 when the decider is the
same principal who raised the request.
{
"error": {
"code": "FORBIDDEN",
"message": "You cannot approve or reject your own price request (BR-433/BR-434): the person who asks and the person who grants must differ. A discount authorised by its own beneficiary is not an authorisation. Ask somebody else holding price_approval:approve to decide it.",
"requestId": "7b770694-1e04-481b-a51a-7fe238c967ce"
}
}A decision is terminal: no PUT, no PATCH, no second decision. A rejection has to carry decision_note; an approval can go without a comment.
Events
Every state change fires its own event: vehicle.published, vehicle.unpublished, vehicle.publish_failed, quote.issued, quote.voided, quote.extended, quote.archived, reservation.created, reservation.voided, reservation.abono_disposed, reservation.converted, sale_note.issued, sale_note.approved, sale_note.voided, purchase_note.issued, purchase_note.corrected, purchase_note.voided, payment.recorded, consignment.created, consignment.returned, consignment.sold, credit_application.recorded, credit_application.submitted, credit_application.decided, credit_application.withdrawn, price_approval.requested and price_approval.decided.
The full catalogue, with each one's data_schema and a sample, is at Subscribable events. How to subscribe and verify the signature is in Webhooks.
Reference
Every resource has its own reference page, generated from the contract. Publications live inside Vehicles.
Quotes · Reservations · Sale notes · Purchase notes · Document payments · Consignments · Credit applications · Price approvals.