VitrinaAPI

Export the vehicle's file to your system

The cost, the reconditioning stage, the papers and the margin, in order.

Car dealershipsOnly in car-dealership workspaces.

An in-house ERP needs a unit's full file: what it cost, what reconditioning stage it's in, what papers are on record, and what it sold for. None of those four pieces of data live in the same place. This recipe makes the full round trip, in the order a real sync asks for them.

Trap

The cost doesn't show up without the exact scope

floor_price_clp (the floor price, your cost) gets omitted from the object for any key without dealership_economics:read: it doesn't arrive as null, it doesn't arrive at all. price_clp (the sale price) carries no such lock: it's the one money figure about a unit that is meant to be seen. If your sync reads the cost and doesn't see it, check the scope before you assume the car has no cost loaded.

Before you start

  • marketplace:read for the vehicle's detail, and dealership_economics:read for the cost.
  • vehicle_registry:read for the documents.
  • sale_notes:read (or :write, if your sync also issues them) for the realised margin.

1. The cost, next to the sale price

curl "https://api.vitrinadev.com/api/v1/vehicles/26a00eb8-62ce-4a02-a9a7-72204ed8a392" \
  -H "Authorization: Bearer $VITRINA_KEY"
{
  "data": {
    "id": "26a00eb8-62ce-4a02-a9a7-72204ed8a392",
    "make": "Toyota",
    "model": "Yaris",
    "version": "XLS 1.5",
    "year": 2022,
    "price_clp": 11990000,
    "floor_price_clp": 9800000,
    "status": "disponible",
    "tenencia": "propio",
    "not_on_lot": false,
    "registration_number": "LBXR21"
  }
}

price_clp is what the buyer sees. floor_price_clp is your cost, the figure the ERP needs for margin. Both live in the same object: it's the same unit, seen with two different permissions, never two calls.

2. What reconditioning stage it's in

Reconditioning is a board (kind: vehicle) just like any sales pipeline, except it moves cars instead of opportunities:

curl -X PUT https://api.vitrinadev.com/api/v1/vehicles/26a00eb8-62ce-4a02-a9a7-72204ed8a392/pipeline \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "pipeline_id": "01a0aee1-b99a-7ab1-8e94-0948d1329ac9" }'
{
  "data": {
    "id": "26a00eb8-62ce-4a02-a9a7-72204ed8a392",
    "pipeline_id": "01a0aee1-b99a-7ab1-8e94-0948d1329ac9",
    "stage_id": "01a0aee1-b9b5-7a75-af15-de9c2e86ae19",
    "stage_entered_at": "2026-09-23T11:45:47.126Z",
    "status": "disponible",
    "prep_assignee_user_id": null
  },
  "meta": { "outcome": "moved" }
}

The operational stage (stage_id, this call) and the commercial status (status) are separate axes. A car in "Mecánica" can be reservado, and one on the board's last column can already be vendido. Your ERP needs to read both if it wants to know where the car is and whether it can be sold.

3. The papers

The full expediente, how it's uploaded and downloaded, is already covered in Keep and read the vehicle file. For a sync, the only new part is the order: list first, then the content of every file you don't already have.

curl "https://api.vitrinadev.com/api/v1/vehicle-attachments?vehicle_id=26a00eb8-62ce-4a02-a9a7-72204ed8a392" \
  -H "Authorization: Bearer $VITRINA_KEY"
{ "data": [] }

An empty expediente is a valid response, not an error. It only means no paper has been uploaded for this unit yet. GET /vehicle-attachments/{id}/content brings back the bytes for each one that does exist, with the same download headers the reference describes (Content-Disposition: attachment, Cache-Control: private, no-store).

4. The margin, once the unit sells

curl -X POST https://api.vitrinadev.com/api/v1/sale-notes \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "vehicle_id": "26a00eb8-62ce-4a02-a9a7-72204ed8a392",
    "seller_of_record": "automotora",
    "net_clp": 11990000,
    "tax_treatment": "afecto",
    "buyer_contact_id": "01a0ce13-d6a9-70cd-8d7f-7345a88df003"
  }'
{
  "data": {
    "id": "f5562449-9f51-4fb3-bd4f-276b6bccaa29",
    "display_id": "V-6",
    "vehicle_id": "26a00eb8-62ce-4a02-a9a7-72204ed8a392",
    "net_clp": 11990000,
    "tax_clp": null,
    "tax_treatment": "afecto",
    "status": "issued",
    "issued_at": "2026-09-23T11:46:24.380Z"
  }
}

The sale note carries no margin field. You calculate it: net_clp from this response, minus floor_price_clp from step 1. sale_note.issued fires when it's issued. A sync listening for webhooks hears about the sale without polling every unit again.

When it fails

Asking for the cost without dealership_economics:read doesn't error. The field isn't in the object at all. Before you report "the car has no cost loaded", confirm the key carries the scope.

In the app: cost is loaded on the car's page, Costos tab, and the reconditioning board is under Stock → Pipelines. Full guide at Automotora manual → Record costs and → Pipelines and reconditioning.

The expediente's full contract, with its six document types, is in Keep and read the vehicle file. That's also where the obligation that comes with receiving a third party's cédula lives.

On this page