Recipe: enrich without a segment
Run any tool against up to 50,000 specific Company targets you name outright — quote, launch, poll, read.
POST /api/v1/enrich accepts an explicit list of Company ids, so there is no population
to import or narrow. The list contains between one and 50,000 opaque Company UUIDs held
by your organization. A registry identifier is not a Company id, and a Company held only
by another organization returns 404 not_found. A large list
can make a multi-megabyte request body. The strict body is
{ tool, input, target, maxCredits, priority?, dryRun, workstreamId? }:
priority is background, normal, or urgent (omitted means normal), and every
authorized agent may choose any tier. Admission is priority-first for free slots; priority
orders future free Run slots but never preempts a running Run; Urgent has no surcharge or
cap. target is { companyIds }; repeated ids collapse, because one target creates one
Run. input must satisfy the selected tool's schema, and maxCredits is required.
1. Read the Legal entity data
POST /api/v1/companies/legal-entity is the bulk paid read for existing Company-plane
ids. It accepts at most 100 opaque Company UUIDs per call and charges one credit for
each Legal entity the organization has not yet been granted. A grant is permanent: later
reads of the same Legal entity do not charge again.
{
"companyIds": ["8a7a0f13-fce0-4d97-a5f8-1950a49e7a63"],
"maxCredits": 1
}curl -X POST https://www.spore.ee/api/v1/companies/legal-entity \
-H "Authorization: Bearer $SPORE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inline-legal-entity-1" \
-d @company-legal-entity.jsonThe quote below also names a second verified Company whose Legal entity has not been read. It is eligible for a Run, but its first priced use must buy the permanent unlock.
2. Quote it
The selected domain-finder reads the Company's name, operating country, and primary
Activity from each target's Core; the caller cannot send or override them. To supply extra
material, set input.context to Catalog field references that resolve to each target's
current Facts; otherwise its input is empty.
{
"tool": "domain-finder",
"intent": "Find domains for the shortlist",
"input": {},
"target": {
"companyIds": [
"8a7a0f13-fce0-4d97-a5f8-1950a49e7a63",
"5c91df6b-3d6f-48e0-9da8-39d551f94a6b"
]
},
"maxCredits": 5,
"priority": "normal",
"dryRun": true
}curl -X POST https://www.spore.ee/api/v1/enrich \
-H "Authorization: Bearer $SPORE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inline-domain-quote-1" \
-d @enrich-quote.json200:
{
"dryRun": true,
"tool": "domain-finder",
"targeted": 2,
"quote": {
"runs": 4,
"unlocks": 1,
"total": 5
},
"refused": []
}The dry run targets one Company unlocked by step 1 and one verified Company whose Legal
entity is still locked. quote.runs prices their Runs; quote.unlocks buys the permanent
right to read the locked Legal entity, once, inside this first priced request;
quote.total is their sum. An unverified target has no Legal entity to unlock. A target
missing required Core is refused at admission with missing_input: it gets no Run and
costs nothing, while refused names the Company and missing slots. targeted and the
quote count only admitted targets. If none can be served, the request returns HTTP 422
missing_input.
3. Launch it
Use the same body with dryRun: false. The new Idempotency-Key is required so a network
retry cannot double-launch or double-charge. The accepted launch preserves the dry-run split.
{
"tool": "domain-finder",
"intent": "Find domains for the shortlist",
"input": {},
"target": {
"companyIds": [
"8a7a0f13-fce0-4d97-a5f8-1950a49e7a63",
"5c91df6b-3d6f-48e0-9da8-39d551f94a6b"
]
},
"maxCredits": 5,
"priority": "normal",
"dryRun": false
}curl -X POST https://www.spore.ee/api/v1/enrich \
-H "Authorization: Bearer $SPORE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inline-domain-1" \
-d @enrich.json201:
{
"job": {
"id": "51e40960-d578-42c6-812c-7c2e21cfc540",
"organizationId": "00000000-0000-4000-8000-000000000010",
"kind": "enrichment",
"intent": "Find domains for the shortlist",
"lifecycle": "waiting",
"priority": "normal",
"occupiedSlots": 0,
"scopeType": "inline",
"segmentId": null,
"companyId": null,
"targetCount": 2,
"runs": {
"waiting": 0,
"queued": 0,
"running": 0,
"succeeded": 0,
"failed": 0,
"cancelled": 0
},
"runTotal": 0,
"quotedCredits": 5,
"maxCredits": 5,
"workstreamId": null,
"createdByUserId": null,
"createdByApiKeyId": "00000000-0000-4000-8000-000000000011",
"startedAt": null,
"completedAt": null,
"cancelRequestedAt": null,
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-08-12T09:27:37.564Z",
"updatedAt": "2026-08-12T09:27:37.564Z"
},
"quote": {
"runs": 4,
"unlocks": 1,
"total": 5
},
"targeted": 2,
"refused": []
}Keep job.id for polling. The Job's ordered target list is its frozen receipt; it does not
depend on a legacy Segment snapshot. The job object is the canonical Job projection: use
its intent, priority, target count, Run counts, and credit fields rather than rebuilding
them from the launch input. job.quotedCredits equals quote.total.
4. Poll the launch
curl https://www.spore.ee/api/v1/enrichments/$LAUNCH_ID \
-H "Authorization: Bearer $SPORE_API_KEY"The launch is progress, not payload: read launch.lifecycle and launch.outcome,
watch launch.completed climb to launch.targeted, and stop at a terminal lifecycle. The
canonical Job's job.lifecycle uses completed_with_issues for a settled Job with failed or
cancelled child Runs; the launch's own projection spells that state
completed_with_errors. The enrichment's top-level lifecycle and outcome describe the
polled row — see Enrichment lifecycle.
5. Read the answers
Because the priced launch permanently unlocked every admitted target, the results land as knowledge on each Company:
curl "https://www.spore.ee/api/v1/company?ids=8a7a0f13-fce0-4d97-a5f8-1950a49e7a63,5c91df6b-3d6f-48e0-9da8-39d551f94a6b" \
-H "Authorization: Bearer $SPORE_API_KEY"knowledge.fields["domain-finder"] holds each value with observedAt, stale, and
sourceRefs — the enrichment ids behind it, each pollable at
GET /api/v1/enrichments/{id} for the full run body. The company read is free.