Entries#
The Entries endpoint is the primary interface for submitting campaign entries programmatically. Each entry is processed asynchronously — you receive 202 Accepted immediately, and lifecycle results arrive via webhooks.
Create an entry#
/v1/entriesSubmit an entry
Accepts an entry for asynchronous processing. Requires a valid API key and an Idempotency-Key header.
Headers
Idempotency-KeystringrequiredCaller-supplied deduplication key (UUID recommended).
Request Body
Example
{ "enrollment": { "document_number": "string", "full_name": "string", "email": "integrator@example.com", "phone": "string", "birthdate": "2026-01-15", "address": { "address_line_1": "string", "address_line_2": "string", "neighborhood": "string", "city": "string", "state": "string", "zipcode": "string", "country": "BR" } }, "consent": { "granted_at": "2026-01-15T12:00:00Z", "ip_address": "string", "user_agent": "string", "term_version": "string", "consents": [ { "consent_type": "regulation", "granted": true } ] }, "evidence": { "type": "fiscal_key", "access_key": "string" }, "instant_win": { "won": true, "prize_definition_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8" }}Schema
enrollmentobjectrequiredEnrollment identity + PII captured for the campaign enrollment. Only ``document_number`` is always required. Whether any other field is mandatory is decided per-campaign by the API channel configuration.
document_numberstringrequiredA valid CPF (11 digits) or CNPJ (14 chars). Punctuation is accepted.
full_nameunknownFull legal name.
emailunknownContact email.
phoneunknownContact phone in E.164 format without the leading `+` (e.g. `5511999998888`). Any country is accepted.
birthdateunknownDate of birth (YYYY-MM-DD).
addressunknownPostal address.
consentobjectrequiredLGPD consent data — required for every API entry.
granted_atstring<date-time>requiredISO 8601 timestamp when consent was granted.
ip_addressstringrequiredIPv4 or IPv6 address of the consenting user.
user_agentstringrequiredBrowser/client User-Agent string at consent time.
term_versionstringrequiredVersion identifier of the accepted terms (e.g. 'v2.1').
consentsarrayPer-type consent grants (e.g. marketing, data_sharing, image_use). Optional — submitted grants are recorded as the participant's explicit choices; the implicit consents (regulation, privacy_policy, transactional) are always recorded.
consents[].consent_typeenum(regulation | privacy_policy | marketing | data_sharing | image_use | transactional | other)requiredFixed catalog of consents a participant can grant during registration. Can be one of the following: regulation, privacy_policy, marketing, data_sharing, image_use, transactional, other.
consents[].grantedbooleanrequiredWhether the participant granted this consent.
evidenceobjectrequiredEvidence submitted with the entry. Currently only FISCAL_KEY is supported.
typestringEvidence type. Only `fiscal_key` is supported today.
access_keystringrequired44–50 digit fiscal access key.
instant_winunknownFor campaigns running an integrator-declared instant-prize mechanic: the win outcome decided for this entry. Recorded atomically with the entry.
Responses
Example
{ "entry_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8", "status": "string", "message": "Entry accepted for processing."}Schema
entry_idstring<uuid>requiredstatusstringrequiredEntry status (e.g. 'PENDING').
messagestringHuman-readable result description.
Headers#
| Header | Required | Description |
|---|---|---|
Authorization | yes | Bearer <api_key> — see Authentication. |
Idempotency-Key | yes | Deduplication key, 1–128 chars — see Idempotency. |
Content-Type | yes | application/json |
Request structure#
The body is composed of three required objects.
Enrollment object#
Participant identity for the campaign enrollment. document_number is the only field that is always required — it identifies the participant. Every other field is optional by default; a campaign may mark additional fields as required (see Required fields below).
The first entry for a document_number creates the enrollment. On later entries, any field left empty on the stored enrollment is filled in from the new submission — but a field that already has a value is kept as-is: a differing value is silently ignored, never overwritten and never rejected.
document_numberstringrequiredA valid CPF (11 digits) or CNPJ (14 characters). Punctuation is accepted and stripped; the checksum is verified. CNPJ participants are only accepted when the campaign permits company participants.
full_namestringThe participant's full legal name. Minimum 3 characters.
emailstringA valid contact email address. Must be unique within the campaign — reusing an email already taken by another participant returns 422.
phonestringA phone number in E.164 format without the leading + (e.g. 5511999998888) — the country code is required and any country is accepted. Punctuation is accepted and stripped. Must be unique within the campaign.
birthdatestring<date>Date of birth in YYYY-MM-DD format. Must be a date in the past.
addressobjectA postal address. All sub-fields are optional: address_line_1, address_line_2, city, state (two-letter Brazilian UF), zipcode, and country (two-letter ISO-3166 code, defaults to BR).
Required fields#
A campaign can require enrollment fields beyond document_number for the API channel — any of full_name, email, phone, birthdate, city, state. A submission that leaves a required field empty and has no stored value for it is rejected with 422. Because requirements are checked against the merged enrollment, a repeat submission may omit a required field once it has already been stored. Sending an address object whose city is empty does not satisfy a city requirement.
Progressive enrollment#
Because stored fields are preserved, you can build the enrollment up over several entries. Send the full identity once — typically on the participant's first entry — then on every later entry send only document_number (plus the required consent and evidence). Evolu matches the existing enrollment and reuses the stored name, email, phone, address, and so on.
First entry — full identity:
{ "enrollment": { "document_number": "11144477735", "full_name": "Maria Silva", "email": "maria@example.com", "phone": "5511999998888" }, "consent": { "granted_at": "...", "ip_address": "...", "user_agent": "...", "term_version": "v2.1" }, "evidence": { "type": "fiscal_key", "access_key": "..." }}Later entries — document_number only:
{ "enrollment": { "document_number": "11144477735" }, "consent": { "granted_at": "...", "ip_address": "...", "user_agent": "...", "term_version": "v2.1" }, "evidence": { "type": "fiscal_key", "access_key": "..." }}The second entry still resolves to Maria's enrollment with all her stored fields intact.
Consent object#
LGPD requirement
The consent object is mandatory for every entry. See LGPD Compliance.
granted_atstring<date-time>requiredA full ISO 8601 timestamp with timezone of when consent was granted, e.g. 2026-04-30T14:30:00Z.
ip_addressstringrequiredA valid IPv4 or IPv6 address of the consenting user.
user_agentstringrequiredThe client User-Agent string at consent time. Stored values over 1024 chars are truncated.
term_versionstringrequiredIdentifier of the accepted terms version, e.g. v2.1.
consentsarrayOptional per-category consent choices — an array of { "consent_type": "...", "granted": bool }. consent_type is one of marketing, data_sharing, image_use, regulation, privacy_policy, transactional, or other. Submitted entries are recorded as the participant's explicit choices; regulation, privacy_policy, and transactional are recorded implicitly even when omitted. See LGPD Compliance.
Evidence object#
typestringrequiredThe evidence type. Only fiscal_key is supported today.
access_keystringrequiredThe 44–50 digit fiscal access key (NF-e / NFC-e / SAT).
Instant-win object#
Optional. Only for campaigns running an integrator-declared instant-prize mechanic — the win you decided for this entry, recorded atomically with it. See Instant Prize.
instant_win.wonbooleanrequiredWhether the participant won an instant prize.
instant_win.prize_definition_idstring<uuid>The prize won — a prize id from the campaign's pool. Required when won is true, omitted when false.
Example request#
curl -X POST https://api.evolu.tec.br/v1/entries \ -H "Authorization: Bearer evolu_live_aB3dE6gH_x7K2p..." \ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \ -H "Content-Type: application/json" \ -d '{ "enrollment": { "document_number": "11144477735", "full_name": "Maria Silva", "email": "maria@example.com" }, "consent": { "granted_at": "2026-04-30T14:30:00Z", "ip_address": "189.50.12.34", "user_agent": "Mozilla/5.0 (Linux; Android 14)", "term_version": "v2.1", "consents": [ { "consent_type": "marketing", "granted": true }, { "consent_type": "data_sharing", "granted": false } ] }, "evidence": { "type": "fiscal_key", "access_key": "35260412345678000195550010000001231123456789" } }'const response = await fetch('https://api.evolu.tec.br/v1/entries', { method: 'POST', headers: { Authorization: `Bearer ${EVOLU_API_KEY}`, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json', }, body: JSON.stringify({ enrollment: { document_number: '11144477735', full_name: 'Maria Silva', email: 'maria@example.com', }, consent: { granted_at: new Date().toISOString(), ip_address: clientIp, user_agent: navigator.userAgent, term_version: 'v2.1', consents: [ { consent_type: 'marketing', granted: true }, { consent_type: 'data_sharing', granted: false }, ], }, evidence: { type: 'fiscal_key', access_key: '35260412345678000195550010000001231123456789', }, }),})const data = await response.json()console.log(data.entry_id)import requests, uuidresponse = requests.post( "https://api.evolu.tec.br/v1/entries", headers={ "Authorization": f"Bearer {EVOLU_API_KEY}", "Idempotency-Key": str(uuid.uuid4()), "Content-Type": "application/json", }, json={ "enrollment": { "document_number": "11144477735", "full_name": "Maria Silva", "email": "maria@example.com", }, "consent": { "granted_at": "2026-04-30T14:30:00Z", "ip_address": client_ip, "user_agent": user_agent, "term_version": "v2.1", "consents": [ {"consent_type": "marketing", "granted": True}, {"consent_type": "data_sharing", "granted": False}, ], }, "evidence": { "type": "fiscal_key", "access_key": "35260412345678000195550010000001231123456789", }, },)print(response.json()["entry_id"])Response#
A successful submission returns 202 Accepted with the entry's tracking id:
{ "entry_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8", "status": "PENDING", "message": "Entry accepted for processing."}entry_idstring<uuid>The unique id of the created (or, on an idempotent replay, the existing) entry. Correlate it with the data.entry_id field of webhook events.
statusstringThe entry status at response time. Usually PENDING; may be REJECTED if the submission failed fast (e.g. an invalid fiscal key).
messagestringA human-readable summary of the result.
Errors#
See Error Handling for the full list of status codes and validation rules — 401 (auth), 403 (registration closed), and 422 (payload validation, campaign configuration, a missing required enrollment field, or an email/phone already used by another participant).
Lifecycle#
After acceptance, the entry progresses through processing and evaluation, emitting entry.received, entry.completed, and entry.evaluated webhooks. See Webhook Events.