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

# Providers that send data

> Connect a Provider that sends its data to Parable: verify its senders, mint an ingest endpoint for each Tap, and see when data arrives.

Most Provider Plugins read the Provider on a schedule. Some Providers send
their data to Parable instead: a webhook, a telemetry exporter, or an agent on
a device. Parable receives each delivery, checks the sender, and lands the
records in the same [Provider Pool](/data/providers.md) tables as data that a
sync reads.

| | A Provider that Parable reads | A Provider that sends data |
| - | - | - |
| `integrationClass` | `api`, `database`, or `file` | `streaming` |
| Who starts a delivery | A scheduled sync | The Provider's sender |
| Credentials | `supportedAuthStrategies`: Parable's credential for the Provider | `supportedInboundAuthStrategies`: how Parable verifies the sender |
| First sync | 90 days after a valid credential save | None |
| Health | `lastSyncAt`, `lastSyncStatus` | `lastDataReceivedAt` |

`protocolType` names the wire protocol, such as `webhook` for webhooks or
`otlp` for OpenTelemetry exporters. Parable Pulse sends data from each Mac
through its own agent protocol. See
[Parable Pulse](https://docs.parable.work/pulse/primer).

## Sender verification

A leaf connection method of such a Provider lists `supportedInboundAuthStrategies`.
Each strategy has an `id`, a `label`, a `verificationType`, a
`handshakeKind`, and a `configSchema` for the secret that verifies the
sender:

| `verificationType` | How Parable checks a delivery |
| - | - |
| `hmac_sha256` | The sender signs each request with a shared secret, for example Slack's signing secret. Parable checks the signature, and can refuse an old timestamp. |
| `bearer_token` | The sender sends `Authorization: Bearer <secret>`. The secret is the verification secret that you saved, not the ingest token. |
| `device_key` | Each device signs its batches with a key that it enrolled. |
| `none` | Only the ingest token in the URL identifies the sender. For senders that cannot send another credential. |

`handshakeKind` says how the sender confirms the endpoint when you register
it: `none`, `slack_url_verification` for Slack's URL verification, or
`ms_graph_validation_token` for a Microsoft Graph subscription. Parable
answers the handshake for you.

## Connect and get an endpoint

1. Create the connection with `POST /api/connectors`, as in
   [Connect a Provider](/connectors/auth.md#connect-a-provider). Send the `id`
   of an inbound strategy as `authenticationStrategyId`, and the verification
   secret that its `configSchema` asks for as `authenticationConfig`.
2. Parable records the credential as valid when you save it, because there is
   no Provider API to check it against. Parable checks the sender on every
   delivery. Saving the credential, then or later, starts no backfill and no
   schedule.
3. List the connection's Taps with
   `GET /api/tenant-connector-taps/tenant-connector-taps?tenantConnectorId=...`.
4. Mint an ingest token for each Tap that a sender delivers to:

   ```bash theme={null}
   curl -X POST "$PARABLE_API_URL/api/ingest-tokens" \
     -H "Authorization: Bearer $PARABLE_API_TOKEN" \
     -H "X-Tenant: $PARABLE_WORKSPACE" \
     -H "Content-Type: application/json" \
     -d '{ "tenantConnectorTapId": "<id of the Workspace Tap>" }'
   ```

   The response has `token`, `endpointUrl`, and `ingestToken`, the token's
   record. `endpointUrl` is the full address that the sender posts to, with
   the token in it.
5. Put `endpointUrl` in the Provider's console or the sender's settings.

Parable shows `token` and `endpointUrl` once, and stores only a hash of the
token. Keep them where the sender's configuration lives. A Tap has at most one
active token. Minting a second one fails while the first is active.

## Manage ingest tokens

| Action | Operation |
| - | - |
| List tokens, newest first, including revoked and expired tokens. Filter with `tenantConnectorTapId`. | `GET /api/ingest-tokens` |
| Mint a token for a Tap | `POST /api/ingest-tokens` |
| Rotate a token. The response has the new `token` and `endpointUrl`. The old token keeps working for 24 hours. | `POST /api/ingest-tokens/{id}/rotate` |
| Revoke a token. A revoked token stops working within a minute. A second revoke changes nothing. | `POST /api/ingest-tokens/{id}/revoke` |

A token record has `prefix`, the first characters of the token, to tell
tokens apart. It also has `lastUsedAt`, `expiresAt`, `rotatedAt`, and
`revokedAt`. Minting, rotating, and revoking need permission to change the
connection's Taps.

To replace a token without a gap, rotate it, update the sender with the new
`endpointUrl`, and let the old token expire.

## Send data

Each receiver has its own path under the endpoint. Every request is a
`POST`.

| Receiver | Path | Largest body |
| - | - | - |
| Webhook | `/v1/{token}` | 256 KiB. With `Content-Encoding: gzip`, 8 MiB on the wire and 8 MiB after Parable decompresses it. |
| OpenTelemetry (OTLP over HTTP) | `/v1/{token}/v1/logs`, `/v1/{token}/v1/metrics` | 8 MiB |

A webhook body is one JSON object, a JSON array of objects, or JSON Lines, one
object on each line. Each request lands every record in it.

Parable answers:

| Status | Meaning |
| - | - |
| `200` | The records landed. The body has `request_id` and `records`, the number of records. |
| `400` | The body is not valid for the receiver. |
| `401` | The token is unknown, revoked, or expired, or the sender check failed. |
| `413` | The body, or the decompressed body, is too large. |
| `415` | The webhook got a `Content-Encoding` other than `gzip` or `identity`, or the OTLP receiver got a content type it does not read. |
| `429` | The sender is over the rate for the token, or Parable is decompressing too many bodies. Wait for `Retry-After`, then send again. |
| `502`, `503` | Parable could not land the records or check the token. Send again. |

The rate for each token is 25 requests a second for a webhook and 200 for
OTLP, unless the plugin sets its own limit.

Send a delivery again when it fails. A plugin keys its records on a stable
ID. When the Tap sets `deduplicateAcrossLoads`, a record that two deliveries
carry keeps one row ([Tap schemas](/connectors/tap-schemas.md#on-the-tap)).

## See when data arrives

When Parable lands data for a token, it stamps the token's `lastUsedAt`, at
most once a minute. A refused request never stamps it. The connection's
`lastDataReceivedAt` is the newest `lastUsedAt` among its tokens. It is null
until the first delivery.

Parable moves landed data into the Provider Pool on a schedule. A query can
read no rows for a short time after a delivery lands.

The [API Reference](/api-reference/overview.md) has the full request and response
shapes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.