Events to your queue or bucket
The same events as the webhooks can reach your infrastructure in two other ways, without exposing an HTTP endpoint:
| Route | What it is for | Latency |
|---|---|---|
| SQS queue or SNS topic | Process events almost in real time in your AWS account | Seconds |
| Daily S3 export (NDJSON) | Feed analytics or a data warehouse with the full day | Once a day |
Neither has its own back-office screen yet: both are configured with the administration API (/api/services/app/...) using an administrator session with the permissions listed. See Admin scopes to automate it with a key.
In the examples, $BASE is your platform's address and $TOKEN your administrator session token.
SQS queue or SNS topic
A webhook subscription can have your SQS queue or your SNS topic as its destination instead of an HTTPS URL. Fan-out, retries, the delivery log and manual redelivery are the same; only the last step differs.
You need the Webhook Subscriptions permission (Pages.Loyalty.Webhooks) and a plan that includes webhooks.
Prepare AWS
- Create the queue (or topic).
.fifoqueues and topics are supported. - Create an IAM access key allowed only
sqs:SendMessageon that queue (orsns:Publishon that topic).
IncentIA does not fall back to its own AWS identity: the credentials are mandatory.
Create the subscription
POST /api/services/app/WebhookSubscription/Create:
curl -s -X POST "$BASE/api/services/app/WebhookSubscription/Create" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Events to SQS",
"destinationType": 1,
"targetUrl": "https://sqs.eu-west-1.amazonaws.com/123456789012/incentia-events",
"awsAccessKeyId": "AKIA...",
"awsSecretAccessKey": "...",
"secret": "optional-signing-secret",
"eventTypes": [2, 14, 18]
}'
| Field | Value |
|---|---|
destinationType | 0 = HTTP (default), 1 = SQS (AwsSqs), 2 = SNS (AwsSns) |
targetUrl | SQS: queue URL, https://sqs.<region>.amazonaws.com/<12-digit account>/<queue>. SNS: topic ARN, arn:aws:sns:<region>:<account>:<topic> |
awsAccessKeyId, awsSecretAccessKey | Required with SQS or SNS |
secret | Optional. If set, every message carries X-Signature |
eventTypes | List of event types (see Lifecycle events) |
- Only the public AWS partition (
aws) is supported. The region comes from the URL or ARN itself. - The AWS secret is stored encrypted and cannot be read back. When editing, leave it empty to keep it. Switching to HTTP deletes the credentials.
What you receive
- Body: exactly the same JSON an HTTP webhook would receive.
- Message attributes: the
X-Event-Type,X-Timestamp,X-Correlation-Id,X-IncentIA-Idempotency-Keyand, with a secret,X-Signatureheaders, as String attributes with the same name and value. The signature is computed as in Verify the signature. - FIFO queues and topics:
MessageGroupIdissubscription-<id>andMessageDeduplicationIdis the idempotency key, so AWS drops a resend it already accepted (5-minute window). Ordering across events is not guaranteed. - Always deduplicate by
X-IncentIA-Idempotency-Key: deliveries may repeat.
Retries and failures
| AWS response | Handling |
|---|---|
| Throttling (HTTP 429) | Retried, like an HTTP 429 |
5xx error or no response | Retried, like an HTTP 5xx |
Any other 4xx error (no permission, unknown queue or topic, invalid key, message too large) | Final failure of that delivery |
| Destination or credentials that are no longer valid | Final failure |
Failures count towards the subscription's automatic deactivation, as with HTTP (see Delivery and retries). The delivery log stores only AWS <code> (HTTP <status>), never AWS's text.
Daily S3 export
Every day a file with all of the previous UTC day's events is dropped in your bucket, with no subscriptions needed. It is a scheduled export of type events.
You need the Scheduled event-stream exports permission (Pages.Exports.Scheduled.Events), in addition to the scheduled exports one. It is checked again for the user who created it on every run.
Content
NDJSON (UTF-8, one JSON line per event, in order):
{"schemaVersion":1,"eventId":"7b1c...","eventType":"ParticipationCreated","event":"participation.created","occurredAt":"2026-11-01T10:15:02Z","recordedAt":"2026-11-01T10:15:02Z","data":{"attemptId":991,"campaignId":17}}
eventIdidentifies the event: deduplicate by it.eventis the dotted name (participation.created); for older events that do not have one, it equalseventType.datais the webhook'sdatawithout personal data: names, email, phone, address, date of birth, IP and so on are removed. Identifiers, codes and amounts remain as join keys.- An export never carries personal data and does not accept
includePii.
Where it lands
One file per day, idempotent. In your bucket:
{prefix}events/dt=YYYY-MM-DD/events_YYYYMMDD.ndjson
Running it again for the same day overwrites that object with the same content (unless a GDPR erasure scrubbed a row in between). The dt= partition is Hive-compatible.
Create the export
POST /api/services/app/ScheduledExport/Create:
curl -s -X POST "$BASE/api/services/app/ScheduledExport/Create" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Daily events",
"exportType": "events",
"format": "ndjson",
"cadence": 1,
"runAtUtcHour": 3,
"destination": 4,
"s3": {
"bucket": "my-bucket",
"region": "eu-west-1",
"prefix": "incentia/",
"accessKeyId": "AKIA...",
"secretAccessKey": "..."
},
"isActive": true
}'
| Field | Notes |
|---|---|
exportType | events, with format ndjson (the only one supported) |
cadence | 1 = daily |
runAtUtcHour | UTC hour to run, 0-23 |
destination | 4 = your own bucket (CustomerS3). 2 (IncentIA's private storage with a link, no bucket needed) and 3 (SFTP) also work. The token link (1) is not supported |
s3.bucket, s3.region | Region is required on AWS |
s3.serviceUrl, s3.forcePathStyle | For S3-compatible storage: the https:// URL of its endpoint |
s3.prefix | Key prefix; empty = bucket root |
s3.accessKeyId, s3.secretAccessKey | Write key. The secret is stored encrypted and not returned |
campaignId, preset, columns | Leave them empty: it is an account-wide export |
Give IncentIA a key that can only s3:PutObject under that prefix. When editing, an empty or *** secret keeps the stored one; changing the bucket, endpoint or key id forces you to enter the secret again.
To run it now, POST /api/services/app/ScheduledExport/RunNow?id=<id>; the history is in GetHistory.
Limits
- The source is the events the platform keeps for 14 days by default: a day that was not exported can only be recovered with
RunNowthe next day, because it always exports the previous day. - An S3-compatible endpoint cannot point to internal or private addresses.
- The Scheduled exports screen (
/app/main/loyalty/exports) lists these exports, but its destination selector does not offer your own bucket.