---
title: "Create Sample Request auto-processing automation"
method: POST
path: "/automations/sample-request"
tags: ["Automations"]
---

# Create Sample Request auto-processing automation

`POST /automations/sample-request`

Auto-approve OR auto-reject incoming TikTok Shop sample requests by criteria. Side-effect: a CRM Group is internally created from the `minimum_performance_criteria` so creator membership stays dynamic. The new `crm_group_id` is returned in `side_effects`. If CRM-group creation fails, the automation still creates (logged + Sentry breadcrumb). Sample request type uses `target_list` (vault list IDs) instead of `creators_to_include`.

## Headers

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

## Request body

- AutomationSampleRequestRequest — Body for POST /automations/sample-request. Maps to DB `automation_type='New Sample Request Processing'`. Backend side-effect: a CRM group is created from `minimum_performance_criteria` so creator membership stays dynamic. The created `crm_group_id` is returned in the response for reference.
  - `automation_name` string, required
  - `sample_request` SampleRequestPayload, required — Inner sample_request config — mirrors the legacy `sample_request_config` shape exactly (the worker reads from it).
    - `action` 'approve' | 'reject', required
    - `rejection_reason` 'NOT_MATCH' | 'OFFLINE' | 'OUT_OF_STOCK' | 'OTHER', nullable
    - `approval_message` string, nullable — DM body sent to the creator on approval. Only meaningful when action='approve' — rejected with reject. Persists as `sample_request_config.approval_message`.
    - `rejection_message` string, nullable — DM body sent to the creator on rejection. Only meaningful when action='reject'. Persists as `sample_request_config.rejection_message`.
    - `message_images` string[] — Optional HTTPS image URLs to attach to the approval / rejection DM. Same URL contract as MessageAddon.image_url — server fetches + re-hosts.
    - `products` SampleRequestProducts, required — Sample-request product targeting. Two modes: * `include_all_products=True` → all shop products eligible; supply `weekly_auto_approval_per_product` for the global per-product cap. * `include_all_products=False` → enumerate `selected_products` with per-product `max_approvals_per_week` caps. Mirrors the legacy `sample_request_config.products` shape exactly so FE-built and API-built automations are interchangeable to the worker.
      - `include_all_products` boolean
      - `selected_products` SampleRequestProductCap[]
        - `product_id` string, required — TikTok Shop product ID (numeric string).
        - `max_approvals_per_week` integer, required — Per-product weekly auto-approval cap. Matches the V2 backend field name (`SampleRequestConfigModels.SelectedProduct.max_approvals_per_week`); 0 = no auto-approvals for this product.
      - `weekly_auto_approval_per_product` integer, nullable — Per-product weekly cap that applies to ALL shop products (only meaningful when `include_all_products=True`).
    - `minimum_performance_criteria` SampleRequestPerformanceCriteria — Min/max performance gate for sample-request approval. All fields are optional — omit a field to skip that side of the bound. API contract uses unprefixed field names + decimal 0-1 scale for rates (`engagement_rate=0.05` for 5%). The translator emits the V2 backend's asymmetric prefix names + 0-100 percent scale at persistence. See modules/automations/SampleRequestConfigModels.py for the V2 shape: - min side → `min_followers`, `min_avg_views`, `min_engagement_rate`, `min_post_rate`, `min_creator_gmv_30_days`, `min_gmv_per_sample` - max side → `maximum_followers`, `maximum_avg_views`, `maximum_engagement_rate`, `maximum_post_rate`, `maximum_creator_gmv`, `maximum_gmv_per_sample`
      - `followers` integer, nullable
      - `avg_views` integer, nullable
      - `engagement_rate` number, nullable — Decimal 0-1 (e.g. 0.05 = 5%). Translator scales to 0-100 percent at persistence.
      - `post_rate` number, nullable — Decimal 0-1 (e.g. 0.5 = 50%). Translator scales to 0-100 percent at persistence.
      - `creator_gmv_30_days` number, nullable — Creator GMV over the trailing 30 days.
      - `gmv_per_sample` number, nullable — GMV per sample sent — gates whether the creator's prior samples drove enough revenue.
    - `maximum_performance_criteria` SampleRequestPerformanceCriteria — Min/max performance gate for sample-request approval. All fields are optional — omit a field to skip that side of the bound. API contract uses unprefixed field names + decimal 0-1 scale for rates (`engagement_rate=0.05` for 5%). The translator emits the V2 backend's asymmetric prefix names + 0-100 percent scale at persistence. See modules/automations/SampleRequestConfigModels.py for the V2 shape: - min side → `min_followers`, `min_avg_views`, `min_engagement_rate`, `min_post_rate`, `min_creator_gmv_30_days`, `min_gmv_per_sample` - max side → `maximum_followers`, `maximum_avg_views`, `maximum_engagement_rate`, `maximum_post_rate`, `maximum_creator_gmv`, `maximum_gmv_per_sample`
      - `followers` integer, nullable
      - `avg_views` integer, nullable
      - `engagement_rate` number, nullable — Decimal 0-1 (e.g. 0.05 = 5%). Translator scales to 0-100 percent at persistence.
      - `post_rate` number, nullable — Decimal 0-1 (e.g. 0.5 = 50%). Translator scales to 0-100 percent at persistence.
      - `creator_gmv_30_days` number, nullable — Creator GMV over the trailing 30 days.
      - `gmv_per_sample` number, nullable — GMV per sample sent — gates whether the creator's prior samples drove enough revenue.
    - `auto_approval_settings` SampleRequestAutoApprovalSettings — Optional auto-approval gating beyond the min/max criteria. * `enabled=False` → manual review for every request that passes the performance gate. Other fields ignored. * `enabled=True` + `always_approve_status` set → auto-approve any creator whose `automation_status` matches; bypass criteria. * `total_weekly_approved_sample_limit` → hard ceiling on weekly auto-approvals across all creators (not per-product). * `always_approve_from_lists_ids` → approve members of these vault lists regardless of criteria. Mirrors `sample_request_config.auto_approval_settings`.
      - `enabled` boolean
      - `total_weekly_approved_sample_limit` integer, nullable
      - `always_approve_status` string, nullable — Auto-approve creators whose top-level `automation_status` matches this value (e.g. `APPROVED`, `LIVE_CREATOR`). Null = no status-based bypass.
      - `always_approve_from_lists_ids` integer[] — Vault list IDs whose members bypass criteria gates and are always approved.
    - `target_list` integer[], required — List IDs (vault_files.resource_id, integer form). Required (≥1) — sample_request type uses target_list for creator selection, NOT creators_to_include.
  - `schedule` AutomationSchedule — Per-day creator-outreach caps + run window. Stored into Automations.schedule JSONB. Same shape as the existing portal payload — keeps the portal and Public API agreeing on schema.
    - `Monday_maxCreators` integer
    - `Tuesday_maxCreators` integer
    - `Wednesday_maxCreators` integer
    - `Thursday_maxCreators` integer
    - `Friday_maxCreators` integer
    - `Saturday_maxCreators` integer
    - `Sunday_maxCreators` integer
    - `start_time` string, nullable — HH:MM 24-hour, in `timezone`. Hours 00-23, minutes 00-59.
    - `end_time` string, nullable — HH:MM 24-hour, in `timezone`. Hours 00-23, minutes 00-59.
    - `timezone` string, nullable — IANA timezone name, e.g. America/Los_Angeles
  - `creators_to_exclude` CreatorsToExclude — Outreach-paradigm recipient exclusion. Mirrors `creators_to_omit` in the legacy DB column / frontend payload, plus the boolean `exclude_previously_messaged` flag from the portal's Exclude Creators panel.
    - `list_upload` string[]
    - `lists_selected` string[]
    - `crm_groups` string[] — UUIDs of existing CRM Groups whose current members should be excluded. Use `GET /crm-groups` to discover.
    - `exclude_previously_messaged` boolean — Skip creators this shop has DM'd before in any prior automation. Maps to config.exclude_previously_messaged.

## Response `201`

Successful Response

- AutomationWriteResponseV2
  - `data` AutomationResourceV2, required — Canonical write-endpoint response. Replaces the v1 AutomationResource (which mirrored the legacy single-shape body). Now reflects the four-type taxonomy — `automation_type` is the public-API type, not the DB string. The DB string is in `_internal_type` for debugging only.
    - `automation_id` integer, nullable — None on dry-run.
    - `shop_id` integer, required
    - `automation_name` string, required
    - `automation_type` string, required — One of: target_collab | dm | email | sample_request.
    - `automation_status` string, required
    - `state` string, required
    - `config` object — Full config as stored. Type-specific.
    - `schedule` object, nullable
    - `creators_to_include` object, nullable
    - `creators_to_exclude` object, nullable
    - `crm_group_id` string, nullable
    - `ai_enabled` boolean
    - `end_date` string, nullable
    - `created_at` string, nullable
    - `updated_at` string, nullable
    - `created_via` string, nullable — Surface that created the automation. Always populated on response: rows with NULL or empty `created_via` (predating migration 020) are surfaced as `portal` since the portal was historically the only writer. Common values: portal | api | api_direct | mcp | agent | slack | internal | campaign_tc_auto (set is not closed — other internal surfaces may appear). `api_direct` marks single-creator invites created by `POST /public/v1/target-collabs`.
    - `side_effects` object, nullable — Backend side-effects, e.g. {'crm_group_created': '<uuid>'} on sample_request creates.
  - `dry_run` boolean
  - `side_effects` object, nullable — Backend side-effects, e.g. {'crm_group_created': 'uuid-...'} for sample_request creates.

## Other responses

- `400` — Validation, missing/invalid Idempotency-Key, or forbidden update field.
- `403` — API key lacks `read_write` scope (`WRITE_NOT_PERMITTED`).
- `404` — Automation/list/group not in this shop.
- `409` — Idempotency conflict or archived row.
- `422` — Validation Error
- `429` — Write-tier rate limit (10/min, 50/hr, 100/day) 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)
