---
title: "Send a Target Collab invite directly"
method: POST
path: "/target-collabs"
tags: ["Target Collabs"]
---

# Send a Target Collab invite directly

`POST /target-collabs`

Send a single TikTok Shop Target Collab (TC) invitation to a creator by handle. This is a convenience surface — it internally creates a single-creator TC automation and immediately starts it.

**Queued semantics.** The response returns immediately with `status: "queued"`. Actual TC delivery happens asynchronously through Reacher's TC delivery worker (browser automation, the same path the portal uses). Typical end-to-end latency is a few seconds, sometimes longer under worker load. Poll `GET /public/v1/automations/{automation_id}` for delivery status and `target_collabs.invitation_id` once the worker submits.

**Not for bulk.** For >10 invites at a time, use `POST /public/v1/automations/target-collab` directly with a multi-creator audience — that's more efficient than calling this endpoint in a loop, and exposes the full TC feature set (content type, sample policy, follow-up DMs).

**Required.** `read_write` scope, `Idempotency-Key` header. Pass `X-Dry-Run: true` to validate without persisting or queueing.

## Headers

- `Idempotency-Key` string, nullable
- `X-Dry-Run` string, nullable

## Request body

- TargetCollabDirectRequest — Body for POST /target-collabs — direct one-shot TC invite. Translates internally to: 1. AutomationTargetCollabRequest with a single-creator `list_upload` audience and a single-product PRODUCTS array, then 2. create_target_collab(... created_via="api_direct"), then 3. _transition_status("running") with audience materialization. Returns 201 with `status: "queued"` — delivery is asynchronous via Reacher's TC delivery worker (browser automation, same path the portal uses). Poll GET /automations/{automation_id} for delivery status.
  - `creator_handle` string, required — TikTok @-handle (without the `@`). Resolved server-side against this shop's CRM / affiliate list (a row in ``crm_creators`` for ``reacher_shop_id=<shop_id>`` whose ``creator_name`` matches the handle, ordered by GMV via a LEFT JOIN against the region's creator table). The creator must already have interacted with your shop — handles for creators who haven't applied / sampled / transacted yet will not resolve, even if the handle exists globally on TikTok. Unknown handle → 404 ``CREATOR_NOT_FOUND``.
  - `product_id` string, required — TikTok Shop product ID to attach to the invitation.
  - `commission_rate` number, required — Affiliate commission rate (0.0-1.0, e.g. 0.20 for 20%).
  - `shop_ads_commission_rate` number, nullable — Optional Shop Ads (co-funded) commission rate (0.0-1.0). When set, persists into the legacy PRODUCTS blob at the per-product level; the TC runtime fills the matching field on the TC form. Omit to leave unset (worker treats as not configured).
  - `message` string, required — TC card message body shown to the creator (max 500 chars). Mirrors `target_collab.message` on the full TC create surface — the portal rejects bodies containing the word `amazon`.
  - `invitation_name` string, required — Name shown on the TC invitation card (max 30 chars). Same field as `target_collab.invitation_name` on the full TC create.
  - `valid_until` string, date, nullable — Date the TC card stops being valid. Defaults to today + 30 days when omitted. After this date the dispatcher stops the underlying automation (see `internal/queries/automations_to_run.py`).
  - `support_contact` TargetCollabDirectSupportContact — Brand-side support contact shown on the TC invitation card. Mirrors `TargetCollabContact` exactly — kept as a separate class so the direct surface's OpenAPI doesn't pull in the heavier automation-shape model name when callers only see this endpoint.
    - `email` string, required — Contact email shown to the creator on the TC card.
    - `phone` string, nullable — E.164 format recommended. Normalized at persistence.

## Response `201`

Successful Response

- TargetCollabDirectResponse — 201 envelope for POST /target-collabs. Queued-semantics surface — the POST returns immediately after persisting the underlying automation row and flipping it to `running`. Actual TC delivery happens asynchronously via the tc_v5 worker (browser automation). Poll GET /automations/{automation_id} for delivery status.
  - `automation_id` integer, required — Underlying Automations.automation_id, for polling.
  - `status` string, required — Always `queued` on success. The underlying automation is `running` immediately; the field name reflects delivery semantics (the TC invite itself is queued for the worker).
  - `creator_id` string, required — Resolved creator_id for the supplied handle.
  - `creator_handle` string, required — The handle echoed back (stripped of any leading `@`).
  - `shop_id` integer, required — Shop the invite was created under.
  - `check_status_at` string, required — Relative URL for polling automation state — `/public/v1/automations/{automation_id}`.
  - `queued_at` string, required — ISO-8601 timestamp at which the start transition fired.
  - `dry_run` boolean — True when X-Dry-Run was set — no automation row was created and no worker job was queued. `automation_id` is `0` and `queued_at` reflects the request time.

## Other responses

- `400` — Validation, missing/invalid Idempotency-Key, or multi-shop scope (`x-shop-id=all`).
- `403` — API key lacks `read_write` scope (`WRITE_NOT_PERMITTED`).
- `404` — creator_handle could not be resolved in this shop's region (`CREATOR_NOT_FOUND`).
- `409` — Idempotency conflict — same key, different body.
- `422` — Validation Error
- `429` — Write-tier rate limit exceeded.

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) · [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/reacherapp/reacher-data-api/revisions/5eefff009b5b/schema)
