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

# Configure a source with a setup link

> Programmatically create a draft source, invite a third party to fill in credentials through a setup link, and get notified via a callback webhook.

This flow lets you build a source configuration experience **on top of the Platform API**: create a source in draft, hand a secure link to a client or third party so they can fill in the connector credentials (without a Nekt account), and receive a webhook when they finish — then validate and finalize the source.

It mirrors the in-app [Share a setup link](/sources/share-setup-link) feature, end to end over the API.

## Overview

```mermaid theme={null}
sequenceDiagram
    participant Client as Your app
    participant API as Nekt API
    participant ThirdParty as Third party
    participant Hook as Your callback URL

    Client->>API: POST /sources/ (draft=true, callback_webhook)
    API-->>Client: Draft source
    Client->>API: POST /sources/{slug}/setup-tokens/
    API-->>Client: { token, expires_at }
    Client->>ThirdParty: Share https://app.nekt.ai/scl/{token}
    ThirdParty->>API: Fill in connector config (via the link)
    API->>Hook: POST callback_webhook (source.setup_filled)
    Client->>API: POST /sources/{slug}/revalidate/
    loop Poll
        Client->>API: GET /connector-validations/{id}/
        API-->>Client: status
    end
    Client->>API: PATCH /sources/{slug}/ (draft=false)
    API-->>Client: Source ready
```

## Prerequisites

* An [API key](/platform-api/introduction#create-an-api-key) for authentication.
* The connector slug for the source type (e.g. `facebook-ads`). See [List Source Connectors](/platform-api/connectors/source).

## Step 1: Create a draft source with a callback webhook

Create the source with `draft: true`. Only `connector_slug` is required for a draft — you (or a third party) fill in the rest later.

Optionally attach a **`callback_webhook`**: Nekt POSTs to it once the third party finishes filling in the configuration through the setup link. Use the optional custom header to authenticate the call on your side.

```bash theme={null}
curl --request POST \
  --url https://api.nekt.ai/api/v1/sources/ \
  --header "x-api-key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "draft": true,
    "connector_slug": "facebook-ads",
    "description": "Client Facebook Ads",
    "callback_webhook": {
      "url": "https://your-app.example.com/hooks/nekt",
      "header_name": "X-Webhook-Secret",
      "header_value": "your-shared-secret"
    }
  }'
```

### `callback_webhook` object

| Field          | Type   | Required | Description                                                                                         |
| -------------- | ------ | -------- | --------------------------------------------------------------------------------------------------- |
| `url`          | string | Yes      | HTTPS URL Nekt POSTs to on setup completion. Must be a public `https://` endpoint.                  |
| `header_name`  | string | No       | Optional custom header name to send with the callback.                                              |
| `header_value` | string | No       | Value for the custom header. Required if `header_name` is set. Stored encrypted and never returned. |

<Note>
  `callback_webhook` is **write-only**. Responses never echo it back; they expose a read-only boolean `has_callback_webhook` so you can confirm one is configured. To remove a configured webhook, send `"callback_webhook": null` on an update.
</Note>

<Warning>
  For security, the URL must use `https` and resolve to a public host. URLs pointing at `localhost`, loopback, private (RFC 1918), or link-local addresses are rejected.
</Warning>

## Step 2: Create a setup token

Generate a time-limited token scoped to this draft source, then build the link as `https://app.nekt.ai/scl/{token}` and share it with the third party.

```bash theme={null}
curl --request POST \
  --url https://api.nekt.ai/api/v1/sources/{source_slug}/setup-tokens/ \
  --header "x-api-key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "expires_in_seconds": 86400 }'
```

```json theme={null}
{
  "id": "0d2c1b47-2902-4c47-8b30-0e7c3fd72609",
  "token": "Qm9vR2k...redactedOnList",
  "expires_at": "2026-06-18T12:00:00Z",
  "created_at": "2026-06-17T12:00:00Z"
}
```

| Field                | Type    | Required | Description                                     |
| -------------------- | ------- | -------- | ----------------------------------------------- |
| `expires_in_seconds` | integer | No       | Token lifetime. Defaults to 24 hours (`86400`). |

<Note>
  The full `token` is returned only on creation. When listing tokens it is redacted. To revoke a link, delete the token: `DELETE /api/v1/sources/{source_slug}/setup-tokens/{id}/`.
</Note>

## Step 3: The third party fills in the configuration

The recipient opens `https://app.nekt.ai/scl/{token}` and enters the connector credentials (or authenticates via OAuth for connectors that support it). No Nekt account is required, and the link is scoped strictly to this draft source.

When they submit, Nekt POSTs to your `callback_webhook` (if configured):

```http theme={null}
POST https://your-app.example.com/hooks/nekt
Content-Type: application/json
X-Webhook-Secret: your-shared-secret

{
  "event": "source.setup_filled",
  "source": {
    "id": "d2c1b472-902c-477a-b300-e7c3fd72609b",
    "slug": "facebook-ads-UcuH",
    "description": "Client Facebook Ads",
    "draft": true,
    "config_completed": false,
    "missing_required_fields": ["access_token"]
  }
}
```

| Field                     | Type    | Description                                                                                                                        |
| ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `config_completed`        | boolean | `true` when all required connector config fields are filled.                                                                       |
| `missing_required_fields` | array   | Dot-paths of required fields still missing (e.g. `report_definition.action_report_time`). Empty when `config_completed` is `true`. |

Use `config_completed` to decide whether you can proceed to validation, or whether you need to send another setup link to collect the remaining fields.

## Step 4: Validate the connector configuration

Start a validation for the draft source. This confirms the credentials work and discovers the available streams. The validation uses the configuration already saved on the draft (from the setup link), so you don't need to send a request body.

```bash theme={null}
curl --request POST \
  --url https://api.nekt.ai/api/v1/sources/{source_slug}/revalidate/ \
  --header "x-api-key: YOUR_API_KEY"
```

<Note>
  You may optionally pass `{ "config": { ... } }` to merge extra fields into the saved configuration before validating.
</Note>

The response returns a validation `id` with status `in_progress`. Poll it until the status is `success` or `failed`:

```bash theme={null}
curl --request GET \
  --url https://api.nekt.ai/api/v1/connector-validations/{validation_id}/ \
  --header "x-api-key: YOUR_API_KEY"
```

To stream the validation logs (useful for surfacing progress or diagnosing failures):

```bash theme={null}
curl --request GET \
  --url "https://api.nekt.ai/api/v1/connector-validations/{validation_id}/logs/" \
  --header "x-api-key: YOUR_API_KEY"
```

## Step 5: Finalize the source

Once validation succeeds, complete the source by setting `draft: false` along with the streams, output layer, and trigger. See [Update Source](/platform-api/sources/update) for the full set of fields.

```bash theme={null}
curl --request PATCH \
  --url https://api.nekt.ai/api/v1/sources/{source_slug}/ \
  --header "x-api-key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "draft": false,
    "output_layer": "OUTPUT_LAYER_ID",
    "trigger": { "type": "manual" },
    "add_streams": [
      {
        "enabled": true,
        "stream_name": "ads",
        "table_name": "facebook_ads_ads",
        "primary_keys": ["id", "updated_time"],
        "replication_key": "updated_time",
        "sync_type": "INCREMENTAL",
        "extract_all_fields": true,
        "fields": null
      }
    ]
  }'
```

After finalizing, you can [Trigger Source](/platform-api/sources/trigger) to start the first extraction.

<Note>
  Setup tokens stop working once the source leaves draft. Any unused link for this source is invalidated when you finalize it.
</Note>

## Customize the OAuth flow

For OAuth connectors (Facebook Ads, Google Analytics GA4, and similar), you can drive the entire setup over the API instead of sharing a setup link, with one exception: the **OAuth consent step**. The provider's login and "Authorize" screen must be opened in a browser by a human once per connected account.

<Warning>
  This is a requirement of the provider (Meta, Google, etc.), not of Nekt. The consent step cannot be made fully server-to-server. Everything else — creating the source, generating the authorization URL, exchanging the code for a token, saving it, and validating — runs through the API.
</Warning>

The authorization code must be exchanged through Nekt's OAuth endpoints. The app's `client_id`/`client_secret` and the registered redirect URIs belong to Nekt, so you cannot exchange the code for a token on your own — the exchange has to go through Nekt's callback.

The steps below use **Facebook Ads** as the example. For GA4, swap `tap-facebook-ads` for `tap-google-analytics-ga4`.

<Steps>
  <Step title="Create a draft source">
    Authenticate with your API key. Create the source with `draft: true` and the connector slug.

    ```bash theme={null}
    curl --request POST \
      --url https://api.nekt.ai/api/v1/sources/ \
      --header "x-api-key: YOUR_API_KEY" \
      --header "Content-Type: application/json" \
      --data '{
        "draft": true,
        "connector_slug": "facebook-ads"
      }'
    ```
  </Step>

  <Step title="Open the consent screen in a browser">
    A human opens the initiate URL, which redirects to the provider's login and authorize screen. Pass your own return URL as `redirect_uri`.

    ```
    GET https://api.nekt.ai/api/v1/tap-facebook-ads/oauth/initiate/?redirect_uri=YOUR_RETURN_URL
    ```
  </Step>

  <Step title="Capture the return">
    After the user authorizes, the provider redirects to your `redirect_uri` with `?code=...&state=...`. Store both values.
  </Step>

  <Step title="Exchange the code for a token">
    Call the callback endpoint with the `code`, `state`, and the **same** `redirect_uri` from step 2. This call uses a user token, not your API key.

    ```bash theme={null}
    curl --request POST \
      --url https://api.nekt.ai/api/v1/tap-facebook-ads/oauth/callback/ \
      --header "Content-Type: application/json" \
      --data '{
        "code": "...",
        "state": "...",
        "redirect_uri": "YOUR_RETURN_URL"
      }'
    ```

    The response returns the `access_token`, `refresh_token`, and related fields.
  </Step>

  <Step title="Save the token on the source">
    Authenticate with your API key. Patch the draft source with the `refresh_token` from the previous step.

    ```bash theme={null}
    curl --request PATCH \
      --url https://api.nekt.ai/api/v1/sources/{source_slug}/ \
      --header "x-api-key: YOUR_API_KEY" \
      --header "Content-Type: application/json" \
      --data '{
        "connector_config": {
          "oauth_credentials": {
            "refresh_token": "REFRESH_TOKEN_FROM_PREVIOUS_STEP"
          }
        }
      }'
    ```

    <Note>
      Nekt injects the `client_id`/`client_secret` at runtime, so you don't need to send them.
    </Note>
  </Step>

  <Step title="Validate">
    Start a validation, then poll it until the status is `success` (see [Step 4](#step-4-validate-the-connector-configuration) above).

    ```bash theme={null}
    curl --request POST \
      --url https://api.nekt.ai/api/v1/sources/{source_slug}/revalidate/ \
      --header "x-api-key: YOUR_API_KEY"
    ```
  </Step>

  <Step title="Finalize the source">
    Patch the source with `draft: false` plus the streams, output layer, and trigger (see [Step 5](#step-5-finalize-the-source) above). You can then [Trigger Source](/platform-api/sources/trigger) to start the first extraction.
  </Step>
</Steps>

<Tip>
  The exact endpoints and request/response shapes are available in the OpenAPI schema at [api.nekt.ai/api/schema/](https://api.nekt.ai/api/schema/).
</Tip>
