Registrar y consultar el consentimiento
POST /v1/consent, lote, idempotencia y lectura de estado e historial.
Este es el endpoint que llama tu banner o tu backend en el momento en que alguien toma una decisión: regístrala una vez y Croombit guarda en bóveda la versión exacta de la cláusula que vio, además de devolverte un justificante firmado al que puedes señalar como prueba.
Registrar
POST /v1/consent
Authorization: Bearer sk_live_…
Idempotency-Key: 4b0f…
{
"subject": "email:user@example.com",
"purpose": "marketing_email",
"state": "GRANTED",
"channel": "banner",
"locale": "es",
"pageUrl": "https://acme.com/signup"
}El subject puede ser un id interno, <tipo>:<valor> o un objeto { "identifiers": [{ "type", "value" }] } para crear el sujeto si no existe. state solo puede ser GRANTED, DENIED o WITHDRAWN.
Respuesta 201: { id, subjectId, subjectCreated, purpose, state, purposeVersion, source, collectedAt, expiresAt, receiptId }. Campos opcionales del cuerpo: channel, locale, country, pageUrl, collectedAt, expiresAt, metadata (cualquier objeto JSON tuyo) y source (más abajo).
Lote
POST /v1/consent/batch con { "records": [ … ] } (máx. 100). Emite un justificante por sujeto que cubre todos sus registros del lote.
Quién actuó: source
Cada registro lleva un source. Es la diferencia entre «la persona aceptó» y «lo afirmamos nosotros», y quien lea la bóveda puede verla:
| `source` | Significado | Quién puede escribirlo |
|---|---|---|
API (por defecto) | Actuó el sujeto: el banner, el centro de preferencias o tu aplicación en su nombre. | Cualquier clave |
IMPORT | Lo afirmas tú, como responsable: una importación de contactos, una anotación del personal. El sujeto no hizo nada. | Solo sk_ |
MIGRATION | Traído desde un sistema anterior. | Solo sk_ |
CONSOLE | Escrito por el propio sistema, por ejemplo una política de caducidad que pasa una concesión a EXPIRED. Tú nunca lo envías. | La plataforma |
Una clave pk_ que envíe IMPORT o MIGRATION recibe 403 secret_key_required: una clave pública solo puede escribir lo que hizo un sujeto. El origen vuelve en la respuesta de escritura, en cada fila del historial, como recordedVia al leer el estado y en los webhooks consent.* (data.source). También queda firmado en el justificante: collection.method es api, import o migration. En un lote, el justificante de un sujeto dice import / migration solo si ninguno de sus registros del lote es del propio sujeto.
Base legal y estado son independientes
La `legalBasis` de un propósito (CONSENT, LEGITIMATE_INTEREST, CONTRACT, LEGAL_OBLIGATION, VITAL_INTEREST, PUBLIC_TASK) dice por qué puedes tratar los datos. El `state` de un registro dice qué se registró. Son campos distintos, y el defaultState de un propósito (NOT_GIVEN, GRANTED o DENIED: lo que lee alguien sin registro) es un tercer ajuste propio: no se deriva de la base legal. Un GRANTED escrito con source: "API" significa que actuó el sujeto; un GRANTED escrito con source: "IMPORT" significa que lo afirmas tú.
Para cargar contactos que ya tienes bajo una base distinta del consentimiento —por ejemplo, clientes existentes bajo un contrato—:
- Crea un propósito cuya
legalBasissea esa base (CONTRACT), por ejemploorder_updates, y publícalo. - Escribe cada contacto con
source: "IMPORT"y tu propiometadata(el lote de importación, la referencia del contrato) para poder acreditar la base más adelante. - Léelos con
recordedVia(estado) osource(historial) para distinguirlos de quienes dieron su consentimiento por sí mismos.
POST /v1/consent/batch
Authorization: Bearer sk_live_…
{ "records": [
{ "subject": "email:ana@example.com", "purpose": "order_updates", "state": "GRANTED",
"source": "IMPORT", "channel": "crm_import",
"metadata": { "importBatch": "2026-09-crm", "contractRef": "C-10482" } }
] }email:, external_id:…): consulta Sujetos e identificadores. El defaultState no sustituye a los registros: cambia lo que lee una persona sin registro, pero no deja prueba individual.Leer el estado
GET /v1/subjects/email:user@example.com/consent?purpose=marketing_email
→ { "subjectId": "…", "purposes": {
"marketing_email": { "state": "GRANTED", "purposeVersion": 3,
"needsReconsent": false, "latestPurposeVersion": 3,
"legalBasis": "CONSENT", "recordedVia": "API" } } }Los propósitos publicados sin registro para el sujeto aparecen con su defaultState y source: "default". Todos los propósitos llevan su legalBasis; recordedVia (API, IMPORT, MIGRATION o CONSOLE) indica cómo entró el último registro. needsReconsent es true si la versión guardada va por detrás de una versión posterior publicada como RECONSENT_REQUIRED, o si el registro ha caducado.
Historial
GET /v1/subjects/{ref}/consent/history?purpose= (solo sk_) devuelve hasta 500 filas de solo anexión, de la más reciente a la más antigua. Cada fila trae purposeKey, purposeVersion, state, source, channel, locale, pageUrl, collectedAt, expiresAt, tu metadata, su proof (el hash de la cláusula exacta con la que se registró) y la cadena supersedesId. Un mapa legalBasis de nivel superior da la base de cada propósito. No se devuelven direcciones IP ni user agents. El historial puede incluir filas EXPIRED escritas por el sistema (source: "CONSOLE"). Para mostrarlo a la persona a quien pertenece, consulta Guía: mostrar a un usuario su propio historial.