Payment method
The workspace's payment provider and its mandate status — what unlocks self-serve add-on activation — and the write that connects it through Fintoc or Mercado Pago.
Download the full API pública projection: openapi.json.
| Method | Path | What it does |
|---|---|---|
GET | /billing/conversations | Billed conversations of one billing month (paginated) |
POST | /billing/conversations/{conversationId}/dispute | Report («Reportar») a billed conversation |
GET | /billing/conversations/export | Export the billed conversations of one month (CSV) |
GET | /billing/history | Billing history: one row per billed month («Historial») |
GET | /billing/history/export | Export the billing history (CSV) |
POST | /billing/invoices/{id}/transfer-claim | «Ya pagué por transferencia»: pause automatic charging of a cobro |
POST | /billing/invoices/{id}/transfer-claim/comprobante | Attach or replace the comprobante of a pending «Ya pagué» |
GET | /billing/invoices/{id}/transfer-claim/comprobantes | The comprobantes attached to a cobro’s «Ya pagué» claims |
GET | /billing/payment-method | Current payment provider + mandate status for the tenant |
POST | /billing/payment-method/setup | Connect a payment provider (Fintoc hosted PAC redirect, or MercadoPago tokenized card) |
GET | /billing/payment-methods | The workspace’s payment methods, or how to pay by transfer |
POST | /billing/payment-methods | Start adding a payment method (PAC or card) |
DELETE | /billing/payment-methods/{id} | Remove a payment method |
POST | /billing/payment-methods/{id}/default | Make an active payment method the default |
GET | /billing/profile | Tenant billing/tax identity (the DTE receptor) |
PUT | /billing/profile | Update the tenant billing/tax identity |
GET | /billing/usage/export | Export the daily usage of one month (CSV) |
GET | /billing/usage/live | Usage of a month: daily meters, amount, month-end estimate, history |
GET /billing/conversations
Billed conversations of one billing month (paginated)
Every conversation that counted toward the month: its billable moment, channel, the contact masked to the last 4 phone digits, its review state and, for a month an invoice billed, whether a later review exempted it (credited on a later invoice). A billed month reads the set the invoice froze at the close (period.source = invoice), so summary.count equals the invoice’s conversations; any other month reads the live meter. Never returns message text. Keyset-paginated (next_cursor). Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
cursor | query | string | no | máx. 200 |
limit | query | integer | no | ≥ 1, ≤ 500 |
curl https://api.vitrinadev.com/api/v1/billing/conversations \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"period": {
"month": "2026-10",
"start": "2026-10-01T03:00:00.000Z",
"end": "2026-11-01T03:00:00.000Z",
"closed": true,
"source": "invoice"
},
"invoice": {
"id": "b1b1b1b1-0000-4000-8000-000000000001",
"number": "INV-00000000-202610",
"status": "issued",
"issued_at": "2026-11-01T09:00:00.000Z"
},
"summary": {
"count": 2,
"unreviewed": 1,
"exempted_after_close": 1,
"conversations_uf": 0.024
},
"items": [
{
"conversation_id": "c1c1c1c1-0000-4000-8000-000000000001",
"display_id": "C-1001",
"first_billable_at": "2026-10-03T14:12:09.000Z",
"channel": "whatsapp",
"contact_last4": "4321",
"review_state": "billable",
"counted_because": "customer_wrote_agent_replied",
"exempted_after_close": null
},
{
"conversation_id": "c1c1c1c1-0000-4000-8000-000000000002",
"display_id": "C-1002",
"first_billable_at": "2026-10-29T22:40:51.000Z",
"channel": "instagram",
"contact_last4": null,
"review_state": "unreviewed",
"counted_because": "customer_wrote_agent_replied",
"exempted_after_close": {
"reason": "El cliente solo saludó y no volvió a responder.",
"exempted_at": "2026-11-02T10:00:00.000Z",
"credited_on_invoice": null
}
}
],
"next_cursor": null
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/conversations/{conversationId}/dispute
Report («Reportar») a billed conversation
The workspace disputes ONE conversation its cobro billed, with a reason code (not_real_customer, duplicate, answered_by_human, other) and an optional comment. One open dispute per conversation (409 otherwise). A dispute never blocks or delays the automatic charge: an approved one is discounted on the next invoice (or refunded). The founder is notified. The conversation list (GET /billing/conversations) carries each conversation’s latest dispute and its status. Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
conversationId | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
reason_code | not_real_customer \ | duplicate \ | answered_by_human \ |
comment | string | — | máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/billing/conversations/<id>/dispute \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason_code": "duplicate",
"comment": "Es la misma persona de ayer."
}'Example response (201)
{
"data": {
"id": "d1d1d1d1-0000-4000-8000-000000000001",
"conversation_id": "c1c1c1c1-0000-4000-8000-000000000001",
"status": "open",
"reason_code": "duplicate",
"reason_label": "Conversación duplicada",
"comment": "Es la misma persona de ayer.",
"resolution": null,
"resolution_note": null,
"created_at": "2026-10-05T14:00:00.000Z",
"resolved_at": null
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/conversations/export
Export the billed conversations of one month (CSV)
The same rows as GET /billing/conversations, the whole month, streamed as UTF-8 CSV (with BOM). Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/billing/conversations/export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/history
Billing history: one row per billed month («Historial»)
Every cobro the workspace was issued (drafts and voided ones excluded) and every month invoiced outside the system, together, newest month first. Each row carries the cobro number, neto, IVA and total in whole CLP, the client-facing status, the paid date, the month’s conversation count (the same count GET /billing/conversations?period= reports) and its documents: the cobro PDF, and — once the cobro is paid and the factura electrónica emitted — the factura folio with its PDF and XML. A cobro issued by hand for a month billed outside the monthly close is an ordinary cobro row on the month it was issued for; another month it also billed reads kind: manual with that cobro in billed_in and its status and documents, but no amount (the amount is on the cobro’s own row). A month invoiced by hand with no cobro on record reads kind: manual, status: billed_outside and no amount. filter = all | paid | open; 12 rows per page, keyset-paginated (next_cursor). Requires billing:read (owners and admins).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
filter | query | all \ | paid \ | open |
cursor | query | string | no | máx. 300 |
limit | query | integer | no | ≥ 1, ≤ 100 |
curl https://api.vitrinadev.com/api/v1/billing/history \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"filter": "all",
"items": [
{
"row_key": "b1b1b1b1-0000-4000-8000-000000000001",
"kind": "invoice",
"period": "2026-10",
"invoice_id": "b1b1b1b1-0000-4000-8000-000000000001",
"number": "VT-2026-0007",
"status": "paid",
"clp_net": 150000,
"iva_amount": 28500,
"clp_total": 178500,
"issued_at": "2026-11-01T12:00:00.000Z",
"due_date": "2026-11-05",
"paid_at": "2026-11-03T15:20:00.000Z",
"paid_date": "2026-11-03",
"conversation_count": 412,
"payable": false,
"billed_in": null,
"concept": null,
"documents": {
"cobro_pdf_path": "/api/v1/billing/invoices/b1b1b1b1-0000-4000-8000-000000000001/pdf",
"factura_state": "issued",
"factura": {
"folio": "1042",
"pdf_path": "/api/v1/billing/invoices/b1b1b1b1-0000-4000-8000-000000000001/factura/pdf",
"xml_path": "/api/v1/billing/invoices/b1b1b1b1-0000-4000-8000-000000000001/factura/xml"
}
}
},
{
"row_key": "manual:2026-09",
"kind": "manual",
"period": "2026-09",
"invoice_id": null,
"number": null,
"status": "billed_outside",
"clp_net": null,
"iva_amount": null,
"clp_total": null,
"issued_at": null,
"due_date": null,
"paid_at": null,
"paid_date": null,
"conversation_count": 388,
"payable": false,
"billed_in": null,
"concept": null,
"documents": {
"cobro_pdf_path": null,
"factura_state": "none",
"factura": null
}
}
],
"next_cursor": null
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/history/export
Export the billing history (CSV)
Every row of GET /billing/history for the filter, one line per month, as UTF-8 CSV with BOM: period, kind, cobro number, neto, IVA, total, status, paid date, conversations, factura folio and the cobro PDF, factura PDF and factura XML links. Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
filter | query | all \ | paid \ | open |
curl https://api.vitrinadev.com/api/v1/billing/history/export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/invoices/{id}/transfer-claim
«Ya pagué por transferencia»: pause automatic charging of a cobro
The owner says this cobro was already paid by bank transfer (for example without its reference, which the reconciliation cannot match). While the claim is pending nothing charges the cobro automatically — not the scheduled charge, not a retry — and it does not advance in collections; the Vitrina team verifies the transfer and either records the payment or resumes the charge with a fresh heads-up. Idempotent (created: false when already pending). 409 when the cobro is no longer open (paid, voided, or a charge is in flight). Requires billing:write.
Optional comprobante (#7094): send the proof of the transfer with the claim as a multipart file part (or the JSON file variant below). A PDF, JPG or PNG of at most 10 MB, type read from the bytes; a refused file (400 comprobante_empty, 413 comprobante_too_large, 415 comprobante_type) refuses the whole request and nothing is claimed. It is a claim, not proof: it changes nothing on the cobro. comprobante in the answer is the file THIS call attached (null without one). If the claim was made but the file could not be stored, the answer is still 200 with comprobante: null and comprobante_error {reason comprobante_not_stored, retryable true}: retry only the file. Add or replace it later with POST …/transfer-claim/comprobante.
Or a JSON body (application/json) — what an MCP connector sends through call_operation, which cannot build multipart: \{ filename, content, content_encoding?, content_type? \} plus the operation's own form fields. content is UTF-8 text by default, or base64 when content_encoding is "base64" (images, PDF, audio, video). The same byte cap applies to the decoded bytes (413 above it); invalid base64 is a 400; the content type defaults from the filename extension.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
filename | string | yes | mín. 1, máx. 255 |
content | string | yes | mín. 1, máx. 35000000 |
content_encoding | utf8 \ | base64 | — |
content_type | string | — | máx. 200 |
curl -X POST https://api.vitrinadev.com/api/v1/billing/invoices/<id>/transfer-claim \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"file": "(binary part)"
}'Example response (200)
{
"data": {
"created": true,
"claimedAt": "2026-10-14T13:20:00.000Z",
"number": "VT-2026-0002"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/invoices/{id}/transfer-claim/comprobante
Attach or replace the comprobante of a pending «Ya pagué»
Adds the proof of the transfer to the cobro’s pending «Ya pagué por transferencia» claim, or replaces the one already there (the old file is deleted). A PDF, JPG or PNG of at most 10 MB; the type is read from the bytes, never from the request (415 comprobante_type; 413 comprobante_too_large; 400 comprobante_empty / comprobante_missing). It is a claim, not proof: it changes nothing on the cobro, its collection or its automatic charge; the Vitrina team checks the transfer. 409 no_pending_claim when the cobro has no pending claim (claim first, or attach the file to the claim itself); 409 comprobante_frozen when the claim’s file already went with a recorded payment; 404 for a cobro of another workspace. Requires billing:write (owners).
Or a JSON body (application/json) — what an MCP connector sends through call_operation, which cannot build multipart: \{ filename, content, content_encoding?, content_type? \} plus the operation's own form fields. content is UTF-8 text by default, or base64 when content_encoding is "base64" (images, PDF, audio, video). The same byte cap applies to the decoded bytes (413 above it); invalid base64 is a 400; the content type defaults from the filename extension.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
filename | string | yes | mín. 1, máx. 255 |
content | string | yes | mín. 1, máx. 35000000 |
content_encoding | utf8 \ | base64 | — |
content_type | string | — | máx. 200 |
curl -X POST https://api.vitrinadev.com/api/v1/billing/invoices/<id>/transfer-claim/comprobante \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"file": "(binary part)"
}'Example response (200)
{
"data": {
"comprobante": {
"id": "5b0e6c1a-9a7d-4c3e-8f21-0c4d2e1f7a90",
"invoice_id": "9a1bd325-3ab8-47e8-afe1-7b87c550b139",
"claimed_at": "2026-11-05T13:31:00.000Z",
"filename": "comprobante_transferencia.pdf",
"mime": "application/pdf",
"size_bytes": 48211,
"uploaded_at": "2026-11-05T13:31:02.000Z",
"state": "pending",
"rejected_at": null,
"received_payment_id": null
},
"replaced": false
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/invoices/{id}/transfer-claim/comprobantes
The comprobantes attached to a cobro’s «Ya pagué» claims
Every file the workspace attached to a «Ya pagué» on this cobro, the newest claim first, with what became of each claim (state). A rejected claim’s file stays here as history. Descriptors only; download one with GET …/comprobantes/\{comprobanteId\}. 404 for a cobro of another workspace. Requires billing:read (owners and admins).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/billing/invoices/<id>/transfer-claim/comprobantes \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "5b0e6c1a-9a7d-4c3e-8f21-0c4d2e1f7a90",
"invoice_id": "9a1bd325-3ab8-47e8-afe1-7b87c550b139",
"claimed_at": "2026-11-05T13:31:00.000Z",
"filename": "comprobante_transferencia.pdf",
"mime": "application/pdf",
"size_bytes": 48211,
"uploaded_at": "2026-11-05T13:31:02.000Z",
"state": "pending",
"rejected_at": null,
"received_payment_id": null
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/payment-method
Current payment provider + mandate status for the tenant
The workspace's chosen payment provider (fintoc for a PAC bank mandate, mercadopago for a tokenized card) and the status of its newest mandate: active means add-ons can be activated self-serve (POST /entitlements/\{feature\}/activate), pending means the Fintoc hosted setup was started but not finished. All three fields are null before any enrollment. Carries no payment instrument data — no card number, bank account or provider token is ever returned.
curl https://api.vitrinadev.com/api/v1/billing/payment-method \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"provider": "fintoc",
"status": "active",
"mandateId": "f1f1f1f1-0000-4000-8000-000000000001"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/payment-method/setup
Connect a payment provider (Fintoc hosted PAC redirect, or MercadoPago tokenized card)
Makes provider the workspace's single payment account and creates its mandate. Fintoc answers status: "pending" with a setupUrl to send the owner to (the hosted PAC setup, which returns to returnUrl); MercadoPago takes a cardToken minted client-side by MercadoPago's own SDK — never a raw card number — and answers status: "active" with setupUrl: null. email (the payer) is required by both providers; rut (optional) goes on the Fintoc customer. Requires billing:write, which only the workspace owner holds.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
provider | fintoc \ | mercadopago \ | transbank_oneclick |
returnUrl | string | — | uri, máx. 2048 |
cardToken | string | — | mín. 1, máx. 256 |
email | string | — | email, máx. 320 |
name | string | — | mín. 1, máx. 256 |
rut | string | — | mín. 1, máx. 32 |
cancelUrl | string | — | uri, máx. 2048 |
bank | string | — | mín. 1, máx. 64 |
curl -X POST https://api.vitrinadev.com/api/v1/billing/payment-method/setup \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "fintoc",
"returnUrl": "https://app.vitrinadev.com/w/agencia/settings/billing/invoices?fintoc=return",
"email": "[email protected]",
"rut": "76.123.456-7"
}'Example response (200)
{
"data": {
"provider": "fintoc",
"status": "pending",
"mandateId": "f1f1f1f1-0000-4000-8000-000000000002",
"setupUrl": "https://pay.fintoc.com/subscriptions/setup/example"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/payment-methods
The workspace’s payment methods, or how to pay by transfer
Every payment method with its rail (fintoc_pac, fintoc_card, oneclick, mercadopago) named by the rail that holds it, its display fields (PAC: bank + last digits + mandate status activo / pendiente_firma / rechazado; card: brand + last 4 + expiry + credit/debit when reported) and which one is the default the cobros are charged to. With no active method pays_by is transfer and transfer carries Vitrina’s bank account and the reference of the cobro to pay. Never returns a card number, a full account number or a provider token. Requires billing:read.
curl https://api.vitrinadev.com/api/v1/billing/payment-methods \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"methods": [
{
"id": "f1f1f1f1-0000-4000-8000-000000000003",
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"status": "activo",
"is_default": true,
"bank": {
"name": "Banco de Chile",
"last4": "9212"
},
"card": null,
"removable": false,
"created_at": "2026-10-02T14:05:00.000Z"
},
{
"id": "f1f1f1f1-0000-4000-8000-000000000004",
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"status": "activo",
"is_default": false,
"bank": null,
"card": {
"brand": "Visa",
"last4": "6623",
"exp_month": null,
"exp_year": null
},
"removable": true,
"created_at": "2026-09-15T10:00:00.000Z"
}
],
"default_method_id": "f1f1f1f1-0000-4000-8000-000000000003",
"pays_by": "method",
"pending_cobro": {
"invoice_id": "f1f1f1f1-0000-4000-8000-000000000010",
"number": "INV-2026-10-0007",
"reference": "VT-7K2QM9",
"due_at": "2026-11-08T03:00:00.000Z"
},
"transfer": null,
"available_rails": [
{
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"recommended": true
},
{
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"recommended": false
}
],
"pac_banks": [
{
"code": "cl_banco_de_chile",
"name": "Banco de Chile"
},
{
"code": "cl_banco_estado",
"name": "BancoEstado"
}
]
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/payment-methods
Start adding a payment method (PAC or card)
Starts the rail’s own enrolment and answers where to send the browser: fintoc_pac → a GET to Fintoc’s hosted PAC checkout; fintoc_card → a GET to Fintoc’s hosted card checkout (credit or debit; offered in available_rails only while Fintoc has the card product enabled for Vitrina, else refused with fintoc_card_unavailable); oneclick → a form POST of TBK_TOKEN to Transbank’s inscription page; mercadopago takes a card token minted by Mercado Pago’s SDK and is active at once (redirect: null). The provider sends the browser back to return_url, which must be on the app’s own origin. The current default stays in place until the new method is active; then it becomes the default and any outstanding cobro is retried on it right away. Requires billing:write.
curl -X POST https://api.vitrinadev.com/api/v1/billing/payment-methods \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"rail": "oneclick",
"return_url": "https://app.vitrinadev.com/w/agencia/settings/billing/invoices?medio=tarjeta"
}'Example response (201)
{
"data": {
"method_id": "f1f1f1f1-0000-4000-8000-000000000005",
"rail": "oneclick",
"status": "pendiente",
"redirect": {
"url": "https://webpay3g.transbank.cl/webpayserver/bp_multicode_inscription.cgi",
"method": "POST",
"fields": {
"TBK_TOKEN": "01ab23cd45ef67ab89cd01ef23ab45cd67ef89ab01cd23ef45ab67cd89ef01ab"
}
}
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /billing/payment-methods/{id}
Remove a payment method
Stops charging the method at once (a Oneclick card is also deleted at Transbank). 409 default_method_has_pending_cobro for the default while a cobro is pending — add or choose another method first — and 409 while a charge is in progress on the method. Removing the default with nothing pending hands the default to the next active method, if any. Answers the updated payment methods. Requires billing:write.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/billing/payment-methods/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"methods": [
{
"id": "f1f1f1f1-0000-4000-8000-000000000003",
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"status": "activo",
"is_default": true,
"bank": {
"name": "Banco de Chile",
"last4": "9212"
},
"card": null,
"removable": false,
"created_at": "2026-10-02T14:05:00.000Z"
},
{
"id": "f1f1f1f1-0000-4000-8000-000000000004",
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"status": "activo",
"is_default": false,
"bank": null,
"card": {
"brand": "Visa",
"last4": "6623",
"exp_month": null,
"exp_year": null
},
"removable": true,
"created_at": "2026-09-15T10:00:00.000Z"
}
],
"default_method_id": "f1f1f1f1-0000-4000-8000-000000000003",
"pays_by": "method",
"pending_cobro": {
"invoice_id": "f1f1f1f1-0000-4000-8000-000000000010",
"number": "INV-2026-10-0007",
"reference": "VT-7K2QM9",
"due_at": "2026-11-08T03:00:00.000Z"
},
"transfer": null,
"available_rails": [
{
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"recommended": true
},
{
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"recommended": false
}
],
"pac_banks": [
{
"code": "cl_banco_de_chile",
"name": "Banco de Chile"
},
{
"code": "cl_banco_estado",
"name": "BancoEstado"
}
]
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /billing/payment-methods/{id}/default
Make an active payment method the default
The cobros are charged to this method from now on, and any outstanding cobro (issued or past due, nothing in flight) is retried on it right away — once per cobro and method. Choosing the current default again changes nothing. 409 for a method that is not active. Answers the updated payment methods. Requires billing:write.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/billing/payment-methods/<id>/default \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"methods": [
{
"id": "f1f1f1f1-0000-4000-8000-000000000003",
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"status": "activo",
"is_default": true,
"bank": {
"name": "Banco de Chile",
"last4": "9212"
},
"card": null,
"removable": false,
"created_at": "2026-10-02T14:05:00.000Z"
},
{
"id": "f1f1f1f1-0000-4000-8000-000000000004",
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"status": "activo",
"is_default": false,
"bank": null,
"card": {
"brand": "Visa",
"last4": "6623",
"exp_month": null,
"exp_year": null
},
"removable": true,
"created_at": "2026-09-15T10:00:00.000Z"
}
],
"default_method_id": "f1f1f1f1-0000-4000-8000-000000000003",
"pays_by": "method",
"pending_cobro": {
"invoice_id": "f1f1f1f1-0000-4000-8000-000000000010",
"number": "INV-2026-10-0007",
"reference": "VT-7K2QM9",
"due_at": "2026-11-08T03:00:00.000Z"
},
"transfer": null,
"available_rails": [
{
"rail": "fintoc_pac",
"rail_name": "Débito automático (PAC)",
"recommended": true
},
{
"rail": "oneclick",
"rail_name": "Tarjeta (Webpay Oneclick)",
"recommended": false
}
],
"pac_banks": [
{
"code": "cl_banco_de_chile",
"name": "Banco de Chile"
},
{
"code": "cl_banco_estado",
"name": "BancoEstado"
}
]
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/profile
Tenant billing/tax identity (the DTE receptor)
«Datos de facturación»: who Vitrina invoices for this workspace — RUT, razón social, giro, address and comuna — and billing_emails, every address that receives the cobros, the payment reminders and collections emails, and the factura electrónica (first one = billing_email). complete says whether a factura can be issued with this data and missing lists what is still needed. Requires billing:read.
curl https://api.vitrinadev.com/api/v1/billing/profile \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"legal_name": "Automotora Ejemplo SpA",
"rut": "76086428-5",
"giro": "Compraventa de vehículos",
"address": {
"line1": "Av. Apoquindo 1234",
"line2": "Of. 501",
"city": "Las Condes",
"region": "Metropolitana de Santiago",
"postal_code": null,
"country": "CL",
"comuna_code": "13114"
},
"billing_email": "[email protected]",
"cc_emails": [
"[email protected]"
],
"billing_emails": [
"[email protected]",
"[email protected]"
],
"complete": true,
"missing": [],
"source": "in_app",
"confirmed_at": "2026-10-07T15:00:00.000Z"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /billing/profile
Update the tenant billing/tax identity
Partial update of «Datos de facturación»: only the fields sent change. The RUT check digit, the SII lengths (razón social 100, giro 40, address 70) and the comuna are validated. billing_emails replaces the whole recipient list (1–10 addresses, in order; repeats dropped). Changes apply to the cobros and facturas issued from now on — an issued factura is never changed — and facturas waiting for this data are re-checked at once. Requires billing:write.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
legal_name | string | null | — | máx. 256 |
rut | string | null | — | máx. 32 |
giro | string | null | — | máx. 256 |
address | object | null | — | |
billing_email | string | null | — | email, máx. 320 |
cc_emails | array | null | — | |
billing_emails | array | null | — |
curl -X PUT https://api.vitrinadev.com/api/v1/billing/profile \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"billing_emails": [
"[email protected]",
"[email protected]"
]
}'Example response (200)
{
"data": {
"legal_name": "Automotora Ejemplo SpA",
"rut": "76086428-5",
"giro": "Compraventa de vehículos",
"address": {
"line1": "Av. Apoquindo 1234",
"line2": "Of. 501",
"city": "Las Condes",
"region": "Metropolitana de Santiago",
"postal_code": null,
"country": "CL",
"comuna_code": "13114"
},
"billing_email": "[email protected]",
"cc_emails": [
"[email protected]"
],
"billing_emails": [
"[email protected]",
"[email protected]"
],
"complete": true,
"missing": [],
"source": "in_app",
"confirmed_at": "2026-10-07T15:00:00.000Z"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/usage/export
Export the daily usage of one month (CSV)
The daily block of GET /billing/usage/live for the same period as UTF-8 CSV (with BOM): one row per Santiago day (fecha), one column per metric (conversaciones, minutos_voz, minutos_desvio, correos_marketing, and uso_<feature> per add-on), then a total row equal to the totals the page shows. Counts only, never conversation content; the billed conversations themselves are GET /billing/conversations/export. Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/billing/usage/export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /billing/usage/live
Usage of a month: daily meters, amount, month-end estimate, history
Without period, the month in progress priced by the same meters and rate card the monthly close uses (what the next close would bill), in UF and in CLP at today's UF; a month-end projection that is an estimate (linear by elapsed Santiago days); every add-on and its inclusion reason; and history: past months as the ISSUED invoice amounts (never recomputed), months invoiced outside the system labelled external. mode says how to read it: fixed-contract workspaces see their fixed price with usage at $0, not-billed workspaces see usage without prices (show_prices false), and a workspace whose billing has not started yet gets billing_starts_on.
With period=YYYY-MM (a past Santiago month, #3760) the same shape describes that month: its meters over the whole month, and for the amount only what was billed — the issued cobro's frozen lines and totals with its frozen UF (amount_source: invoice), a month invoiced by hand (external) or nothing (none, usage only); never today's prices. A cobro that froze its billed set counts conversations from it (conversations_source: invoice), so the count equals the cobro's. projection is null for a past month. A future month is a 400.
daily carries every metric per Santiago day (conversations, voice and forwarded minutes, billed agent-eval runs, email-campaign sends, per-unit add-on usage); each series' total is the sum of its days. GET /billing/usage/export is the same as CSV.
next_charge (current month only; null for a past month or when prices are hidden) is the cobro the close will issue: its cut-off, issue and charge dates, whether it is charged automatically or paid by transfer, and its lines in pesos (so far and at month end) exactly as the hosted cobro will show them. Counts and money only, never conversation content. Requires billing:read.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/billing/usage/live \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"as_of": "2026-10-07T15:00:00.000Z",
"period": {
"month": "2026-09",
"start": "2026-09-01T04:00:00.000Z",
"end": "2026-10-01T03:00:00.000Z",
"elapsed_days": 30,
"total_days": 30,
"closed": true
},
"mode": "metered",
"billing_starts_on": null,
"show_prices": true,
"uf": {
"clp": 39485.65,
"date": "2026-10-01",
"stale": false
},
"usage": {
"conversations": 3,
"voice_minutes": 4,
"forwarded_voice_minutes": 0,
"eval_runs": 0,
"email_marketable_contacts": 0,
"email_marketing_sent": 0
},
"lines": [
{
"kind": "conversations",
"count": 3,
"uf_unit": 0.012,
"uf_subtotal": 0.036
},
{
"kind": "voice_minutes",
"count": 4,
"uf_unit": 0.006,
"uf_subtotal": 0.024
}
],
"totals": {
"subtotal_uf": 0.06,
"clp_net": 2369,
"iva_amount": 450,
"clp_total": 2819
},
"projection": null,
"next_charge": null,
"amount_source": "invoice",
"invoice": {
"id": "b1b1b1b1-0000-4000-8000-000000000001",
"number": "INV-00000000-202609",
"status": "paid",
"issued_at": "2026-10-01T09:00:00.000Z"
},
"conversations_source": "invoice",
"daily": {
"days": [
"2026-09-01",
"2026-09-02",
"2026-09-03"
],
"series": [
{
"metric": "conversations",
"feature": null,
"total": 3,
"values": [
1,
0,
2
]
},
{
"metric": "voice_minutes",
"feature": null,
"total": 4,
"values": [
0,
4,
0
]
},
{
"metric": "forwarded_voice_minutes",
"feature": null,
"total": 0,
"values": [
0,
0,
0
]
},
{
"metric": "eval_runs",
"feature": null,
"total": 0,
"values": [
0,
0,
0
]
},
{
"metric": "email_marketing_sent",
"feature": null,
"total": 0,
"values": [
0,
0,
0
]
}
]
},
"addons": [],
"history": []
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429