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

# Company enrichment (streaming)

> Trigger company-profile enrichment and stream the suggestions as they resolve over Server-Sent Events

Company enrichment fills in a tenant's profile — firmographics, logo, website-derived attributes, certifications — from external sources. The streaming contract lets a client resolve those fields **live** rather than waiting for a single batch to finish: a fast first layer returns within about a second, and a deeper layer streams in behind it.

The flow has two calls: a short `POST` to trigger (or reuse) a job, followed by a long-lived `GET` that streams suggestions over [Server-Sent Events](https://developer.mozilla.org/docs/Web/API/Server-sent_events). Both require the `OWNER` or `ADMIN` role.

## Start a stream

Triggers a fresh enrichment job if the last one is stale (older than seven days), or returns the in-flight job if one is already running. The response is minimal — you only need the `jobId` to correlate, and `alreadyRunning` to know whether you joined an existing job.

```bash theme={null}
curl -X POST https://api.linkxg.com/api/v1/company-enrichment/stream/start \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

**Response:**

```json theme={null}
{
  "success": true,
  "data": {
    "jobId": "uuid",
    "alreadyRunning": true
  }
}
```

This endpoint has no side effects beyond triggering the job. Open the stream next to receive results.

***

## Open the stream

```bash theme={null}
curl -N https://api.linkxg.com/api/v1/company-enrichment/stream \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: text/event-stream"
```

The response is a `text/event-stream`. Suggestions arrive in two layers:

1. **Attio layer (\~1s)** — firmographics and logo from the CRM. If Attio is unavailable, this layer is skipped with an `enrichment_status` event carrying `attioSkipped: true`; it is never a fatal error.
2. **Pipeline layer (\~45s)** — website and document extraction suggestions.

### Event types

All event names are prefixed `enrichment_` so they never collide with other streams (such as the in-product assistant) on the same client.

| Event                   | Meaning                                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
| `enrichment_status`     | A layer changed state (`running`, `complete`, `partial`, `failed`); may carry `attioSkipped: true`. |
| `enrichment_suggestion` | One suggested field value. The SSE `id:` field carries a resume cursor.                             |
| `enrichment_complete`   | All layers are done; the stream then closes.                                                        |
| `enrichment_error`      | A layer-scoped (non-fatal) or stream-scoped (fatal, then closes) error.                             |
| `heartbeat`             | Keep-alive, emitted every 15 seconds.                                                               |

**Example stream:**

```
event: enrichment_status
data: {"layer":"attio","state":"running"}

id: 2026-06-24T09:00:01.200Z/01J8...
event: enrichment_suggestion
data: {"id":"uuid","attributeCode":"employee_count","value":"250","sourceType":"ATTIO"}

event: heartbeat
data: {}

event: enrichment_complete
data: {"layers":{"attio":"complete","pipeline":"complete"}}
```

### Resuming

The stream is resumable. On reconnect, send the cursor from the last `enrichment_suggestion` you received in the `Last-Event-ID` header. The database is the source of truth, so a client that reconnects resumes without re-emitting already-seen suggestions and without losing any.

```bash theme={null}
curl -N https://api.linkxg.com/api/v1/company-enrichment/stream \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: text/event-stream" \
  -H "Last-Event-ID: 2026-06-24T09:00:01.200Z/01J8..."
```

### Rate limits

The stream allows 10 connection opens per company per minute and a maximum of 3 concurrent streams per company. Exceeding either returns `429`.

***

## Confirm a suggestion

Each suggestion is acted on individually — accept it, edit the value, or dismiss it. The write-through is idempotent: repeating the same action returns `alreadyConfirmed: true` without writing twice. A *conflicting* transition on a field that was already `ACCEPTED` or `EDITED` returns `409`, so a prior write-through is never silently undone.

```bash theme={null}
curl -X POST https://api.linkxg.com/api/v1/company-enrichment/suggestions/SUGGESTION_ID/confirm \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "action": "ACCEPT" }'
```

For an edit, include the corrected value:

```bash theme={null}
  -d '{ "action": "EDIT", "editedValue": "Acme Holdings Ltd" }'
```

**Response:**

```json theme={null}
{
  "success": true,
  "data": {
    "suggestionId": "uuid",
    "status": "ACCEPTED",
    "writtenTo": "COMPANY_COLUMN",
    "coverageScore": 68,
    "alreadyConfirmed": false,
    "checklistItemCompleted": true
  }
}
```

`action` is one of `ACCEPT`, `EDIT`, `DISMISS`. `writtenTo` indicates where an accepted value landed (`COMPANY_COLUMN`, `COMPANY_ATTRIBUTE`, `CERTIFICATION`, or `null` for a dismissal). `coverageScore` is the tenant's updated profile completeness.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Adoption state" icon="signs-post" href="/developers/adoption">
    Read the tenant's adoption journey and next-best-action
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference">
    Browse the complete API documentation
  </Card>
</CardGroup>
