> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.recurrente.com/referencia-api/api-reference/service-tabs/add-service-tab-items/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recurrente.com/_mcp/server. # Agregar items a una cuenta abierta POST https://app.recurrente.com/api/service_tabs/{service_tab_id}/items Content-Type: application/json Agrega uno o más consumos a la cuenta. Cada item queda registrado por separado en el feed de actividad, igual que la cuenta que ve el cliente. La respuesta trae la cuenta completa con su nuevo total. Un item nombra algo que ya vendes (`price_id` o `product_id`) o se define solo, con `name` y `amount_in_cents`. Son las mismas tres formas que aceptan los items de un checkout. Los precios recurrentes no se aceptan: una cuenta abierta solo cobra productos de pago único. Reference: https://docs.recurrente.com/referencia-api/api-reference/service-tabs/add-service-tab-items ## Authentication - `X-SECRET-KEY` header (required) — Tu clave secreta de API. Una llave de cuenta (`sk_live_...`, `sk_test_...`) opera sobre su propia cuenta y, con `X-ACCOUNT-ID`, sobre sus cuentas conectadas. La llave también fija el ambiente: una `sk_test_` solo lista y resuelve objetos de prueba (`live_mode: false`) y una `sk_live_` solo objetos reales. Un ID del otro ambiente responde `404` (`401` en `/customers`). Una llave de organización (`sk_org_live_...`, `sk_org_test_...`) alcanza todas las cuentas de una organización y solo sirve para leer: saldos, movimientos y reportes. Para operar sobre una cuenta debe nombrarla con `X-ACCOUNT-ID`; omitirlo en una lectura devuelve todas las cuentas de la organización. Cualquier otro endpoint responde `403` con `code: organization_key_unsupported`. ## Request ### Path parameters - `service_tab_id` (string, required) — ID de la cuenta abierta ### Body (application/json) This endpoint expects an object. - `items` (list of ServiceTabsServiceTabIdItemsPostRequestBodyContentApplicationJsonSchemaItemsItems, required) ## Response ### 201 Items agregados - `id` (string, optional) — ID de la cuenta abierta - `status` (enum, optional) — Estado de la cuenta. `open` acepta items; `closing` ya está cerrada y espera el pago; `paid` se cobró; `voided` se anuló sin cobrar; `abandoned` se dejó vencer. - Allowed values: `open`, `closing`, `paid`, `voided`, `abandoned` - `display_name` (string, optional) — Nombre con el que el comercio identifica la cuenta (mesa, cliente, cuarto) - `phone` (string, optional, nullable) — Teléfono del cliente al momento de abrir la cuenta - `customer_id` (string, optional) — ID del cliente dueño de la cuenta - `currency` (string, optional) — Moneda de la cuenta - `total_in_cents` (integer, optional) — Total consumido hasta ahora, en centavos - `payment_strategy` (enum, optional) — `card_on_file` cobra al cerrar la tarjeta guardada del cliente. `preauthorization` mantiene un hold del monto estimado y solo se puede activar desde el dashboard, porque requiere una autorización real del procesador. - Allowed values: `card_on_file`, `preauthorization` - `opening_commitment` (enum, optional) — Qué se comprometió al abrir la cuenta. Se infiere de la solicitud: `products` si mandaste `items`, `manual_amount` si mandaste `estimated_amount`, `none` si no mandaste ninguno. - Allowed values: `none`, `manual_amount`, `product`, `products` - `items` (list of ServiceTabItem, optional) — Líneas vigentes de la cuenta (los items anulados no aparecen) - `authorized_amount_in_cents` (integer, optional) — Monto preautorizado con hold sobre la tarjeta, en centavos. `0` cuando no hay preautorización. - `authorization_expires_at` (datetime, optional, nullable) — Momento en que expira el hold de la preautorización - `checkout_url` (string, optional, nullable) — Checkout que cierra la cuenta; pagarlo la marca como `paid`. Solo aparece mientras la cuenta se está cobrando (`closing` o `paid`): si un cobro con tarjeta se declina, la cuenta se reabre y el checkout de ese intento no se publica, porque el cliente no podría pagarlo. - `opened_at` (datetime, optional) — Momento en que se abrió la cuenta - `closed_at` (datetime, optional, nullable) — Momento en que se cerró la cuenta - `created_at` (datetime, optional) ## Errors ### 422 Unprocessable Entity Error No se enviaron items, no son válidos para una cuenta abierta, o la cuenta ya está cerrada - `message` (string, optional) — Mensaje de error - `code` (string, optional) — Código estable legible por máquinas, cuando aplica - `refund_id` (string, optional) — ID del reembolso relacionado cuando aplica - `endpoint` (string, optional) — Acción de API rechazada, cuando aplica - `errors` (ErrorErrors, optional) — Detalles de errores por campo ## Types ### ServiceTabsServiceTabIdItemsPostRequestBodyContentApplicationJsonSchemaItemsItems Nombra algo que ya vendes (`price_id` o `product_id`) o define el consumo en la misma llamada con `name` y `amount_in_cents` — el cobro manual de un descorche. - `price_id` (string, optional) — Precio existente a cobrar - `product_id` (string, optional) — Producto existente; se cobra su precio vigente - `name` (string, optional) — Nombre del cobro manual, cuando no mandas `price_id` ni `product_id` - `amount_in_cents` (integer, optional) — Monto del cobro manual, en centavos - `currency` (string, optional) — Moneda del cobro manual. Por defecto, la de la cuenta abierta. Una cuenta no puede mezclar monedas: un item en otra moneda se rechaza con `422`. - `quantity` (integer, optional, default: 1) — Entero mayor a 0. Un lote con un item inválido no agrega ninguno. ### ServiceTabItem - `id` (string, optional) — ID del item, firmado y estable mientras el item exista - `price_id` (string, optional) — ID del precio cobrado en esta línea - `name` (string, optional) — Nombre del producto - `quantity` (integer, optional) — Cantidad - `amount_in_cents` (integer, optional) — Total de la línea (precio × cantidad), en centavos - `currency` (string, optional) — Moneda de la línea ### ErrorErrors Detalles de errores por campo ## Examples **Request** ```json { "items": [ {} ] } ``` **Response** ```json { "id": "st_du7dnfwt", "status": "open", "display_name": "Mesa 4", "phone": "+50255551234", "customer_id": "cus_3jfrywsf", "currency": "GTQ", "total_in_cents": 12500, "payment_strategy": "card_on_file", "opening_commitment": "products", "items": [ { "id": "it_eyJfcmFpbHMiOnsi", "price_id": "price_hguxwfto", "name": "Cerveza", "quantity": 2, "amount_in_cents": 5000, "currency": "GTQ" } ], "authorized_amount_in_cents": 0, "authorization_expires_at": "2024-01-15T09:30:00Z", "checkout_url": "https://app.recurrente.com/checkout-session/ch_ccydxmvm8fvhmxsn", "opened_at": "2024-01-15T09:30:00Z", "closed_at": "2024-01-15T09:30:00Z", "created_at": "2024-01-15T09:30:00Z" } ``` **SDK Code** ```python import requests url = "https://app.recurrente.com/api/service_tabs/service_tab_id/items" payload = { "items": [{}] } headers = { "X-SECRET-KEY": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://app.recurrente.com/api/service_tabs/service_tab_id/items'; const options = { method: 'POST', headers: {'X-SECRET-KEY': '', 'Content-Type': 'application/json'}, body: '{"items":[{}]}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://app.recurrente.com/api/service_tabs/service_tab_id/items" payload := strings.NewReader("{\n \"items\": [\n {}\n ]\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-SECRET-KEY", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://app.recurrente.com/api/service_tabs/service_tab_id/items") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-SECRET-KEY"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"items\": [\n {}\n ]\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://app.recurrente.com/api/service_tabs/service_tab_id/items") .header("X-SECRET-KEY", "") .header("Content-Type", "application/json") .body("{\n \"items\": [\n {}\n ]\n}") .asString(); ``` ```php request('POST', 'https://app.recurrente.com/api/service_tabs/service_tab_id/items', [ 'body' => '{ "items": [ {} ] }', 'headers' => [ 'Content-Type' => 'application/json', 'X-SECRET-KEY' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://app.recurrente.com/api/service_tabs/service_tab_id/items"); var request = new RestRequest(Method.POST); request.AddHeader("X-SECRET-KEY", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"items\": [\n {}\n ]\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-SECRET-KEY": "", "Content-Type": "application/json" ] let parameters = ["items": [[]]] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://app.recurrente.com/api/service_tabs/service_tab_id/items")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```