curl --request POST \
--url https://api.example.com/products \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "02",
"taxes": [],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001",
"livemode": true
}
'import requests
url = "https://api.example.com/products"
payload = {
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": True,
"taxability": "02",
"taxes": [],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001",
"livemode": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
description: 'Ukelele',
productKey: 60131324,
price: 345.6,
taxIncluded: true,
taxability: '02',
taxes: [],
localTaxes: [],
unitKey: 'H87',
unitName: 'Elemento',
sku: 'UKL-001',
livemode: true
})
};
fetch('https://api.example.com/products', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/products",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'description' => 'Ukelele',
'productKey' => 60131324,
'price' => 345.6,
'taxIncluded' => true,
'taxability' => '02',
'taxes' => [
],
'localTaxes' => [
],
'unitKey' => 'H87',
'unitName' => 'Elemento',
'sku' => 'UKL-001',
'livemode' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/products"
payload := strings.NewReader("{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/products")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/products")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "590ce6c56d04f840aa8438af",
"organizationId": "org-uuid",
"livemode": false,
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "01",
"taxes": [
{
"type": "IVA",
"rate": 0.16,
"withholding": false,
"factor": "Tasa"
}
],
"localTaxes": [
{
"type": "IVA",
"rate": 0.16,
"withholding": false,
"factor": "Tasa"
}
],
"unitKey": "H87",
"unitName": "Elemento",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"sku": "UKL-001"
}Create Product
curl --request POST \
--url https://api.example.com/products \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "02",
"taxes": [],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001",
"livemode": true
}
'import requests
url = "https://api.example.com/products"
payload = {
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": True,
"taxability": "02",
"taxes": [],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001",
"livemode": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
description: 'Ukelele',
productKey: 60131324,
price: 345.6,
taxIncluded: true,
taxability: '02',
taxes: [],
localTaxes: [],
unitKey: 'H87',
unitName: 'Elemento',
sku: 'UKL-001',
livemode: true
})
};
fetch('https://api.example.com/products', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/products",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'description' => 'Ukelele',
'productKey' => 60131324,
'price' => 345.6,
'taxIncluded' => true,
'taxability' => '02',
'taxes' => [
],
'localTaxes' => [
],
'unitKey' => 'H87',
'unitName' => 'Elemento',
'sku' => 'UKL-001',
'livemode' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/products"
payload := strings.NewReader("{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/products")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/products")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"description\": \"Ukelele\",\n \"productKey\": 60131324,\n \"price\": 345.6,\n \"taxIncluded\": true,\n \"taxability\": \"02\",\n \"taxes\": [],\n \"localTaxes\": [],\n \"unitKey\": \"H87\",\n \"unitName\": \"Elemento\",\n \"sku\": \"UKL-001\",\n \"livemode\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "590ce6c56d04f840aa8438af",
"organizationId": "org-uuid",
"livemode": false,
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "01",
"taxes": [
{
"type": "IVA",
"rate": 0.16,
"withholding": false,
"factor": "Tasa"
}
],
"localTaxes": [
{
"type": "IVA",
"rate": 0.16,
"withholding": false,
"factor": "Tasa"
}
],
"unitKey": "H87",
"unitName": "Elemento",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"sku": "UKL-001"
}This endpoint takes noorganizationIdin the URL. Authenticate with either a Supabase Bearer session (send theX-Organization-Id: <org-id>header — the user must be a member of that organization) or an API key (the organization resolves automatically from the key; anyX-Organization-Idheader sent alongside an API key is ignored).
Authentication
Accepts either:- A Supabase Bearer session (
Authorization: Bearer <token>) with theX-Organization-Id: <org-id>header — the authenticated user must be a member of that organization. - An API key (
X-API-Key: sk_...) with thewrite:productsscope — the organization is resolved from the key itself.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
description | string | Yes | — | Product or service description |
productKey | integer | Yes | — | SAT product/service key (clave de producto o servicio) |
price | number | Yes | — | Unit price |
taxIncluded | boolean | No | false | Whether taxes are already included in the price. Only supported when the only transferred tax is IVA at a rate (Tasa); withholdings are allowed. |
taxability | string | No | derived | SAT taxability code (objeto de impuesto). If omitted: "02" when taxes is not empty, "01" otherwise. Must agree with taxes: "02" requires at least one tax; "01", "03" and "04" must have none. |
taxes | array | No | [] | List of applicable taxes |
localTaxes | array | No | [] | Not supported yet — must be empty ([]); a non-empty list is rejected with a 400. |
unitKey | string | No | "H87" | SAT unit of measure key (clave de unidad) |
unitName | string | No | "Elemento" | Unit of measure name |
sku | string | No | — | Internal product SKU or identifier |
livemode | boolean | No | true | Whether this is a live mode product |
Tax Object
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Tax type (e.g. "IVA", "IEPS") |
rate | number | Yes | Tax rate as a decimal (e.g. 0.16) |
withholding | boolean | No | Whether it’s a withholding tax |
factor | string | No | Factor type (e.g. "Tasa") |
Example Request
curl -X POST https://api.timbrix.mx/products \
-H "Authorization: Bearer <your_token>" \
-H "X-Organization-Id: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "02",
"taxes": [{ "type": "IVA", "rate": 0.16 }],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001"
}'
import { Timbrix } from "@timbrix/sdk"
const client = new Timbrix({ bearerToken: "<your_token>" })
const product = await client.products.create(
{
description: "Ukelele",
productKey: 60131324,
price: 345.6,
taxIncluded: true,
taxability: "02",
taxes: [{ type: "IVA", rate: 0.16 }],
localTaxes: [],
unitKey: "H87",
unitName: "Elemento",
sku: "UKL-001",
},
"550e8400-e29b-41d4-a716-446655440000"
)
console.log(product)
Example Response
{
"id": "590ce6c56d04f840aa8438af",
"organizationId": "550e8400-e29b-41d4-a716-446655440000",
"livemode": true,
"description": "Ukelele",
"productKey": 60131324,
"price": 345.6,
"taxIncluded": true,
"taxability": "02",
"taxes": [{ "type": "IVA", "rate": 0.16 }],
"localTaxes": [],
"unitKey": "H87",
"unitName": "Elemento",
"sku": "UKL-001",
"createdAt": "2025-01-26T10:00:00Z",
"updatedAt": "2025-01-26T10:00:00Z"
}
Common Errors
400 Bad Request
Missing required fields, invalid values, or a missingX-Organization-Id header on a session-authenticated request.
Also returned when taxability and taxes don’t agree (e.g. "01" with IVA), or when taxIncluded is true with a transferred tax other than IVA at a rate:
{
"statusCode": 400,
"message": "La configuración de impuestos del producto no es congruente: el objeto de impuesto \"01\" no admite impuestos."
}
{
"statusCode": 400,
"message": [
"productKey must be a positive integer",
"price must be a positive number"
]
}
401 Unauthorized
Missing or invalid Bearer token / API key.403 Forbidden
The authenticated user is not a member of the organization sent inX-Organization-Id, or the API key does not have the write:products scope.
409 Conflict
A product with the same SKU already exists in this organization.{
"statusCode": 409,
"message": "A product with SKU \"UKL-001\" already exists in this organization"
}
Authorizations
API Key for authentication (format: sk_...)
Headers
Required for Supabase session auth. Ignored when authenticating with an API key (the organization resolves from the key).
Body
Product or service description
"Ukelele"
SAT product/service key (clave de producto o servicio)
60131324
Unit price
345.6
Whether taxes are already included in the price
true
SAT taxability code (objeto de impuesto). If omitted, it is derived: "02" when taxes is not empty, "01" otherwise. Must agree with taxes: "02" requires at least one tax; "01", "03" and "04" must have none.
"02"
Applicable taxes
Show child attributes
Show child attributes
Applicable local taxes
Show child attributes
Show child attributes
SAT unit of measure key (clave de unidad)
"H87"
Unit of measure name
"Elemento"
Internal SKU or identifier
"UKL-001"
Whether this is a live mode product
true
Response
"590ce6c56d04f840aa8438af"
"org-uuid"
false
"Ukelele"
60131324
345.6
true
"01"
Show child attributes
Show child attributes
Show child attributes
Show child attributes
"H87"
"Elemento"
"UKL-001"