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

# Attach ghost handles to creators

> Keep contract IDs and analytics history when you link tracked handles to existing SideShift creators.

Use these operations when a handle was imported before its creator was known.
Both preserve the existing contract ID, post IDs, and daily analytics history.

| Your goal                                                         | Operation                                                                                                               | Required scope    |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Group tracked views under a creator without enabling payments     | [Attach an existing ghost handle](/platform/oauth/campaigns/attach-an-existing-ghost-handle-to-a-creator)               | `campaigns:write` |
| Turn tracking into a creator contract that the creator can accept | [Convert to a pending creator contract](/platform/oauth/campaigns/convert-a-ghost-handle-to-a-pending-creator-contract) | `contracts:write` |

One creator can have several attached ghost handles, within one campaign or across
campaigns. Call the operation once per ghost contract with the same `creatorId`.
Each ghost contract has one creator assignment. These endpoints do not move a
handle from one real creator to another or merge two existing creator contracts.

## Before you start

Use a [scoped Platform API key](/self-use) or [OAuth access token](/quickstart).
Both use `https://app.sideshift.app/api/oauth/v1`. Legacy API keys for `/api/v1`
cannot call these routes. Your company must have an active subscription.

You need three SideShift IDs:

* **Campaign ID:** the campaign that contains the ghost handle.
* **Ghost contract ID:** the `contractId` returned when that handle was created.
* **Creator ID:** the creator's user ID, such as an `id` returned by
  [List creators](/platform/oauth/creators/list-creators). Do not use a social
  handle, a contract ID, or an ID from your previous provider.

The creator must already be saved in your company's Creator Database or have a
real contract with your company. List creators returns contracted creators; it
does not search the full SideShift user directory. For a database-only creator,
use the SideShift user ID from your existing mapping or the platform.

The campaign and ghost contract must both belong to the credential's company.
For agency subaccounts, use the normal [act-as flow](/platform/agency-access).
Do not send a `companyId` in the body.

## Attach for analytics

```bash theme={"system"}
curl --request POST \
  --url 'https://app.sideshift.app/api/oauth/v1/campaigns/CAMPAIGN_ID/ghost-handles/GHOST_CONTRACT_ID/attach' \
  --header 'x-api-key: YOUR_PLATFORM_API_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: migration-contract-123-attach' \
  --data '{"creatorId":"CREATOR_ID"}'
```

For OAuth, replace `x-api-key` with `Authorization: Bearer YOUR_ACCESS_TOKEN`.
Send only one authentication method.

```json theme={"system"}
{
  "data": {
    "contractId": "GHOST_CONTRACT_ID",
    "creatorId": "CREATOR_ID",
    "isGhostHandle": true,
    "contractStatus": "active",
    "postsUpdated": 12,
    "alreadyApplied": false
  }
}
```

Attachment changes creator attribution on the contract, its posts, and the matching
campaign handle. Tracking and analytics history remain in place. The contract stays
a ghost with payments disabled and uses no creator seat. Analytics filters that
exclude ghost handles still exclude these posts.

If you have not created the handle yet, you can pass `creatorId` when you
[add the ghost handle](/platform/oauth/campaigns/add-a-ghost-handle-to-a-campaign)
instead of making a separate attachment request.

## Convert to a creator contract

Use the same body and authentication with the `/convert` endpoint and a new
idempotency key:

```bash theme={"system"}
curl --request POST \
  --url 'https://app.sideshift.app/api/oauth/v1/campaigns/CAMPAIGN_ID/ghost-handles/GHOST_CONTRACT_ID/convert' \
  --header 'x-api-key: YOUR_PLATFORM_API_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: migration-contract-123-convert' \
  --data '{"creatorId":"CREATOR_ID"}'
```

A new conversion returns `isGhostHandle: false` and `contractStatus: "pending"`.
It uses the campaign's payment terms. It does not sign or activate the contract,
send an invitation message, or pay the creator. The creator must accept through
the normal platform flow. Normal approval and seat checks apply at activation.

Complete any required brand-owned document fields before conversion. The API
does not accept signatures, payment terms, or arbitrary contract fields in this
request. If the creator already has an active or pending real contract in the
campaign, conversion returns `409` without changing either contract. Use analytics
attachment if you only need the extra handles attributed to that creator.

Existing posts and tracking dates are preserved. Review the campaign's payment
terms and payout start before activation: conversion does not reset historical
content or create a new tracking period.

## Batch migrations and retries

Keep a mapping of your previous provider's record, campaign ID, ghost contract ID,
and creator ID. Process one handle per request and record each successful response.

* Use a distinct `Idempotency-Key` for each operation and ghost contract. Keys must
  contain 1–200 characters.
* After a timeout or `500`, retry with the same key, IDs, and body. A completed
  keyed request returns its original response.
* Reusing a key with a different campaign, contract, or body returns
  `409 idempotency_conflict`.
* A request still in progress returns `409`. Wait before retrying it. A
  `RESOURCE_CHANGED` conflict can be retried with the same request after the
  campaign or contract change is complete.
* Respect `Retry-After` on `429`. Process conversions in a campaign sequentially
  to reduce conflicts from concurrent campaign updates.

Contract attribution, campaign handles, and post attribution commit together in
the source database. A failed transaction changes none of them. Analytics read
models and cached totals can take time to reflect the change.

## Supported contracts

These endpoints support unpaid, active or pending, tracking-only ghost **handle**
contracts with at most **450 posts** each. A larger contract is rejected before
any write. Contact support for larger migrations.

Ghost videos, retired creator contracts, and contracts with payment or acceptance
history are not supported. Sandbox credentials cannot make these changes.

| Response | Meaning                                                                                                                   |
| -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `400`    | Invalid IDs or body, missing/oversized idempotency key, more than 450 posts, or incomplete required brand fields.         |
| `401`    | Missing or invalid credentials.                                                                                           |
| `402`    | The company needs an active subscription.                                                                                 |
| `403`    | Missing write scope or sandbox credentials.                                                                               |
| `404`    | Campaign, contract, or eligible creator not found in this company. Foreign resources return the same response.            |
| `409`    | Conflicting assignment, existing creator contract, unsupported contract state, changed resource, or idempotency conflict. |
| `429`    | Rate limit reached. Wait for the indicated retry interval.                                                                |

See [API errors](/platform/errors) for the standard error envelope.
