Purchases API
Registrá una compra de forma idempotente y obtené el resultado de loyalty asociado.
Operaciones
/api/purchasesRecord a purchase and return the resulting loyalty state
The public request is deliberately narrower than runtime compatibility. Identical Idempotency-Key replay returns the same transaction without duplicate loyalty effects.
Scopes requeridos
purchases.writeParámetros y headers
x-request-idheaderopcionalOptional caller correlation identifier.
{
"type": "string"
}X-Tenant-IDheaderopcionalOptional consistency hint; it never replaces the credential tenant.
{
"maximum": 9007199254740991,
"minimum": 1,
"type": "integer"
}X-Tenant-SubdomainheaderopcionalOptional consistency hint; it must resolve to the credential tenant.
{
"type": "string"
}Idempotency-KeyheaderrequeridoRequired purchase idempotency key. Runtime trims the value; it must remain nonblank after trimming and may be reused only for the identical purchase payload.
{
"maxLength": 128,
"minLength": 1,
"pattern": ".*\\S.*",
"type": "string"
}Request body
requerido
{
"additionalProperties": false,
"properties": {
"currency_code": {
"maxLength": 3,
"minLength": 3,
"pattern": "^[A-Za-z]{3}$",
"type": "string"
},
"customer_id": {
"maximum": 9007199254740991,
"minimum": 1,
"type": "integer"
},
"external_reference": {
"minLength": 1,
"pattern": ".*\\S.*",
"type": "string"
},
"program_id": {
"maximum": 9007199254740991,
"minimum": 1,
"type": "integer"
},
"total_amount": {
"exclusiveMinimum": 0,
"type": "number"
}
},
"required": [
"customer_id",
"program_id",
"total_amount",
"currency_code",
"external_reference"
],
"type": "object"
}Respuestas
HTTP 200 — Purchase processed or identical request replayed.
{
"additionalProperties": false,
"properties": {
"data": {
"additionalProperties": false,
"properties": {
"balance": {
"type": "number"
},
"currentTier": {
"oneOf": [
{
"additionalProperties": false,
"properties": {
"id": {
"minimum": 1,
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
{
"type": "null"
}
]
},
"externalReference": {
"type": [
"string",
"null"
]
},
"idempotentReplay": {
"type": "boolean"
},
"pointsEarned": {
"type": "number"
},
"previousTier": {
"oneOf": [
{
"additionalProperties": false,
"properties": {
"id": {
"minimum": 1,
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
{
"type": "null"
}
]
},
"tierChanged": {
"type": "boolean"
},
"transactionId": {
"minimum": 1,
"type": "integer"
}
},
"required": [
"transactionId",
"pointsEarned",
"balance",
"previousTier",
"currentTier",
"tierChanged",
"idempotentReplay",
"externalReference"
],
"type": "object"
},
"ok": {
"const": true
}
},
"required": [
"ok",
"data"
],
"type": "object"
}HTTP 400 — Purchase request validation or tenant-hint error.
Códigos de error
ambiguous_tenant_hintinvalid_currencyinvalid_idempotency_keyinvalid_jsoninvalid_purchaseinvalid_tenant_hintmissing_idempotency_key{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"missing_idempotency_key",
"invalid_idempotency_key",
"invalid_json",
"invalid_purchase",
"invalid_currency",
"invalid_tenant_hint",
"ambiguous_tenant_hint"
],
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 401 — Missing or invalid API key.
Códigos de error
invalid_api_keymissing_api_key{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"missing_api_key",
"invalid_api_key"
],
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 403 — Scope, tenant or program boundary denial.
Códigos de error
insufficient_scopeprogram_not_allowedtenant_mismatch{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"insufficient_scope",
"tenant_mismatch",
"program_not_allowed"
],
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 404 — Customer not found in the authoritative tenant context.
Códigos de error
customer_not_found{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"const": "customer_not_found"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 409 — Program/currency mismatch or idempotency conflict/in-progress condition.
Códigos de error
currency_mismatchidempotency_conflictidempotency_in_progressprogram_mismatch{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"program_mismatch",
"currency_mismatch",
"idempotency_conflict",
"idempotency_in_progress"
],
"type": "string"
},
"details": {
"additionalProperties": false,
"properties": {
"transactionId": {
"minimum": 1,
"type": "integer"
}
},
"type": "object"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 429 — Generic rate-limit denial; no numeric limit or retry timing is promised.
Códigos de error
rate_limited{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"const": "rate_limited"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}HTTP 500 — Internal or reconciliation-required purchase failure. Reconcile before retry when code is purchase_reconciliation_required.
Códigos de error
customer_lookup_failedinternal_errorprogram_lookup_failedpurchase_lookup_failedpurchase_reconciliation_requiredtier_lookup_failed{
"additionalProperties": false,
"properties": {
"error": {
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"purchase_lookup_failed",
"customer_lookup_failed",
"program_lookup_failed",
"tier_lookup_failed",
"purchase_reconciliation_required",
"internal_error"
],
"type": "string"
},
"details": {
"additionalProperties": false,
"properties": {
"reason": {
"type": "string"
},
"transactionId": {
"minimum": 1,
"type": "integer"
}
},
"type": "object"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"ok": {
"const": false
}
},
"required": [
"ok",
"error"
],
"type": "object"
}Ejemplos probados
Purchase idempotency conflict
- HTTP esperado
- 409
- Última prueba
- 2026-08-11
curl --request POST \
--url 'https://api.qualth.com/api/purchases' \
--header 'X-API-Key: {{api_key}}' \
--header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
--header 'Idempotency-Key: {{new_idempotency_key}}' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": {{new_customer_id}},
"program_id": {{program_id}},
"total_amount": 1600,
"currency_code": "ARS",
"external_reference": "{{new_external_reference}}"
}'Purchase identical replay
- HTTP esperado
- 200
- Última prueba
- 2026-08-11
curl --request POST \
--url 'https://api.qualth.com/api/purchases' \
--header 'X-API-Key: {{api_key}}' \
--header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
--header 'Idempotency-Key: {{new_idempotency_key}}' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": {{new_customer_id}},
"program_id": {{program_id}},
"total_amount": {{purchase_amount}},
"currency_code": "ARS",
"external_reference": "{{new_external_reference}}"
}'Purchase record
- HTTP esperado
- 200
- Última prueba
- 2026-08-11
curl --request POST \
--url 'https://api.qualth.com/api/purchases' \
--header 'X-API-Key: {{api_key}}' \
--header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
--header 'Idempotency-Key: {{new_idempotency_key}}' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": {{new_customer_id}},
"program_id": {{program_id}},
"total_amount": {{purchase_amount}},
"currency_code": "ARS",
"external_reference": "{{new_external_reference}}"
}'