> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sparkles.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Process and reconcile events

> Understand Sparkles partner event types, ordering, attribution, and replay recovery.

Sparkles emits an ordered event stream for each API access grant. Webhooks carry one event per request; the event feed returns the same durable events for gap recovery.

## Event envelope

Every durable event contains:

| Field       | Type             | Description                                                  |
| ----------- | ---------------- | ------------------------------------------------------------ |
| `id`        | UUID             | Stable across webhook retries and event-feed reconciliation. |
| `sequence`  | Positive integer | Monotonically increasing cursor within the access grant.     |
| `type`      | String           | One of the event types below.                                |
| `data`      | Object           | Event-specific payload.                                      |
| `createdAt` | ISO 8601 string  | When Sparkles recorded the event.                            |

Process events in numeric `sequence` order and deduplicate on `id`.

## Credit claim events

`credit_claim.created` fires after Sparkles creates and reserves a unique claim. Its `status` is `pending` and `claimedAt` is `null`.

`credit_claim.claimed` fires after Sparkles adds the credits once to the recipient's selected organization. Its `status` is `claimed` and `claimedAt` contains an ISO 8601 timestamp.

Both payloads contain:

| Field          | Type                          |
| -------------- | ----------------------------- |
| `claimId`      | UUID                          |
| `leadId`       | String                        |
| `variant`      | String                        |
| `creditAmount` | Positive whole-credit integer |
| `status`       | `pending` or `claimed`        |
| `createdAt`    | ISO 8601 string               |
| `claimedAt`    | ISO 8601 string or `null`     |

## Attributed lifecycle events

All lifecycle payloads contain `claimId`, `leadId`, `variant`, and `occurredAt`.

| Event                 | Additional fields                                | Fires when                                                                                                      |
| --------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `project.connected`   | `projectId`                                      | The attributed recipient connects a new repository-backed project after claiming the offer.                     |
| `task.started`        | `taskId`, `projectId` or `null`                  | Sparkles accepts and durably creates the recipient's cloud task. The agent runtime may still be starting.       |
| `pull_request.opened` | `pullRequestId`, `taskId`, `projectId` or `null` | Sparkles durably records an opened pull request for the attributed task.                                        |
| `conversion.paid`     | `conversionId`                                   | The attributed organization completes its first positive initial subscription invoice. Renewals do not emit it. |

Attribution is immutable first touch. Project, task, and pull-request events use the first claimed offer for the exact recipient and selected organization. Paid conversion uses the first claimed offer for the selected organization. Redeeming a later claim does not replace either attribution.

## Recover a sequence gap

Use the event feed only for backfills or webhook gap recovery:

```bash theme={null}
curl "https://sparkles.dev/api/public/v1/events?after=41&limit=100" \
  --header "Authorization: Bearer $SPARKLES_API_KEY"
```

```json theme={null}
{
	"data": [
		{
			"id": "06ea989d-3980-4f83-bb58-93ad29ae8409",
			"sequence": 42,
			"type": "project.connected",
			"data": {
				"claimId": "01890a5d-ac96-774b-bcce-b302099a8057",
				"leadId": "lead_123",
				"variant": "call-offer",
				"projectId": "aaf58b9e-a967-4fde-a82f-e7d574e11a6a",
				"occurredAt": "2026-08-03T10:12:00.000Z"
			},
			"createdAt": "2026-08-03T10:12:00.000Z"
		}
	],
	"nextCursor": 42
}
```

Save `nextCursor` only after processing every returned event successfully, then pass it as the next `after` value. The default and maximum `limit` is `100`. An empty page preserves the supplied cursor.

## Recovery loop

1. Read your last durable cursor.
2. Request events with `after=<cursor>`.
3. Process each event in ascending sequence order.
4. Deduplicate each event by UUID.
5. Save `nextCursor` after the batch commits.
6. Repeat until `data` is empty.

See [error handling](/errors) for rate-limit and retry behavior.
