Saltar al contenido
Qualth
API Reference

Purchases API

Registrá una compra de forma idempotente y obtené el resultado de loyalty asociado.

Operaciones

POST/api/purchases

Record 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.write

Parámetros y headers

x-request-idheaderopcional

Optional caller correlation identifier.

{
  "type": "string"
}
X-Tenant-IDheaderopcional

Optional consistency hint; it never replaces the credential tenant.

{
  "maximum": 9007199254740991,
  "minimum": 1,
  "type": "integer"
}
X-Tenant-Subdomainheaderopcional

Optional consistency hint; it must resolve to the credential tenant.

{
  "type": "string"
}
Idempotency-Keyheaderrequerido

Required 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}}"
}'