Skip to main content
POST
/
api
/
enrichment
Enrich Leads
curl --request POST \
  --url https://app.puffle.ai/api/enrichment \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "enrichmentId": "<string>",
  "mode": "persisted",
  "scope": {
    "type": "explicit_leads",
    "leadIds": [
      "<string>"
    ],
    "listId": "<string>"
  },
  "input": {},
  "output": {
    "attributeKey": "<string>"
  },
  "displayRequest": {
    "fieldId": "<string>"
  },
  "idempotencyKey": "<string>"
}
'
{
  "enrichmentRunId": "<string>",
  "runId": "<string>",
  "status": "<string>",
  "leadCount": 4503599627370495,
  "triggerRunId": "<string>",
  "reusedExistingRun": true
}
CLI:
puffle enrichment create --enrichment-id <enrichment-id> --scope <scope>
puffle enrichment create --enrichment-id <enrichment-id> --scope <scope> --mode <mode> --input <input> --output <output> --display-request <display-request> --idempotency-key <idempotency-key> --write-policy <write-policy>

Overview

Starts an Enrichment for a Lead scope and dispatches a background worker. The endpoint is asynchronous: it validates the request, gates credits, persists the execution ledger, enqueues the Trigger.dev task, and returns the enrichmentRunId immediately. Poll Enrichment status until it is terminal, then read per-Lead results with Get enriched leads. The handler previews credits before dispatch. If the workspace cannot afford the request, it returns 402 with { required, balance } and no work is created. Successful dispatch charges result.leadCount credits against the Enrichment’s creditAction.

Lead scope

Pick one of three scope shapes in the scope field:
  • { type: "explicit_leads", leadIds: [...], listId?: "..." } — enrich a specific set of Leads. listId is optional and only used for membership-scoped output.
  • { type: "list", listId: "..." } — enrich every Lead currently in the List.
  • { type: "filtered_list", listId: "...", filter: {...} } — enrich Leads in the List that match a LeadFilter. Filtered scopes are not yet supported by the planner; use one of the other two shapes.

Modes

  • mode: "persisted" (default) — runs the worker and writes results to Lead or membership attributes. Charges credits.
  • mode: "preview" — plans the Enrichment without dispatching a worker or writing anything. Free.
Pass idempotencyKey to safely retry. A repeat call with the same key returns the existing execution (reusedExistingRun: true) instead of creating a duplicate or double-charging.
See also: Enrichment status · Get enriched leads · Cancel enrichment.

AI agent notes

The enrichment journey:
  1. Call Get enrichments to find the right enrichmentId and read its input schema.
  2. POST /api/enrichment with { enrichmentId, scope, input, output? }. Include idempotencyKey for any retryable flow.
  3. GET /api/enrichment/{id} — poll every 2 to 5 s until status is completed, failed, or cancelled.
  4. GET /api/enrichment/{id}/items — read per-Lead results, filter by ?status=failed to triage problems.
Pre-flight checks:
  • Read the Enrichment’s subject (person or company) from Get enrichments, then choose Leads of that type. Do not add a subject field inside scope; the scope only accepts type, leadIds, listId, and optional filter.
  • Use mode: "preview" for a dry run that returns the resolved Lead count without dispatching or charging.
  • enrichmentRunId and runId in the response are the same value — runId is kept as a compatibility alias.
On 400: the code field carries the planner rejection (e.g. EMPTY_LEAD_SCOPE, SUBJECT_MISMATCH, OUTPUT_TARGET_UNSUPPORTED). Surface code in error handling so callers can recover programmatically.On 402: insufficient credits. The response carries required and balance so you can ask the human to top up. No work is created.On 502: code: "DISPATCH_FAILED" means the execution row was created but Trigger.dev rejected the dispatch — the status appears as failed immediately. Correlate triggerRunId with Puffle Seer and the Trigger dashboard.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
enrichmentId
string
required
Minimum string length: 1
mode
enum<string>
default:persisted
required
Available options:
persisted,
preview
scope
object
required
input
object
required
output
object
displayRequest
object
idempotencyKey
string
writePolicy
enum<string>
Available options:
overwrite,
skip_existing

Response

Enrichment started or reused.

enrichmentRunId
string
required
runId
string
required
status
string
required
leadCount
integer
required
Required range: 0 <= x <= 9007199254740991
triggerRunId
string | null
required
reusedExistingRun
boolean
required