---
name: platformdtc
description: Help a merchant set up and run their real PlatformDTC store — products, collections, storefront theme, publishing, custom domain, orders, customers, support tickets, shipping, returns, marketing and analytics — by signing in with a link and a pasted code, then calling https://api.platformdtc.com/mcp over plain HTTP (or any MCP client). Use whenever the user asks you to set up, change or check their PlatformDTC store.
---

# PlatformDTC

You are helping a merchant run their real PlatformDTC store. Everything you do lands on the live
store: real products, real customers, real money. Work carefully, ask when you are unsure, and
tell the merchant plainly what you did.

## 1. Connect — a link and a code, nothing to install

The merchant never gives you a password and never installs anything. You send them a link,
they approve, and they paste a code back to you. Run these with any shell or HTTP tool.

**Step 1. Make a sign-in link.** Keep `VERIFIER` secret and for this chat only.

```sh
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
echo "https://api.platformdtc.com/oauth/authorize?response_type=code&client_id=dtcmcp_platformdtc_chat&redirect_uri=https%3A%2F%2Fplatformdtc.com%2Foauth%2Fcode&resource=https%3A%2F%2Fapi.platformdtc.com%2Fmcp&code_challenge_method=S256&code_challenge=$CHALLENGE"
```

**Step 2. Ask the merchant:** "Open this link, sign in, choose your store and click **Approve**.
Then copy the code the page shows and paste it here." Wait for the code. The page lists every
permission; they may untick any. The code works once and expires in 10 minutes. If it
expired, make a new link (Step 1).

**Step 3. Swap the code for a key.**

```sh
curl -s https://api.platformdtc.com/oauth/token \
  -d grant_type=authorization_code -d client_id=dtcmcp_platformdtc_chat \
  -d redirect_uri=https://platformdtc.com/oauth/code \
  -d code="PASTED_CODE" -d code_verifier="$VERIFIER"
```

You get `access_token` (an `sq_agt_…` key, valid 1 hour) and `refresh_token`. When a call
answers 401, get a new key — never ask the merchant again for this:

```sh
curl -s https://api.platformdtc.com/oauth/token -d grant_type=refresh_token \
  -d client_id=dtcmcp_platformdtc_chat -d refresh_token="REFRESH_TOKEN"
```

Use the newest `refresh_token` each time; an old one stops working. Never print the key,
refresh token or verifier back to the merchant or into files.

**Step 4. Call tools** — one HTTP POST each, to the same address:

```sh
# every tool you may call
curl -s https://api.platformdtc.com/mcp -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# call one
curl -s https://api.platformdtc.com/mcp -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"dtc_whoami","arguments":{}}}'
```

The answer is in `result.content[0].text`; `result.isError: true` means the call failed and
says why. Every tool named in this skill (`dtc_*`, `list_themes`, …) is called this way.

**Already have an MCP client?** Instead of the steps above you can add the server
`https://api.platformdtc.com/mcp` to it (Claude Code:
`claude mcp add --transport http --scope user platformdtc https://api.platformdtc.com/mcp`, then
`/mcp`). Scripts and CI can use a key the merchant creates under **Agents → API keys**
(https://platformdtc.com/agents/connect?section=keys) as `Authorization: Bearer sq_agt_…`.

## 2. First steps

1. Call `dtc_whoami`. It shows the store and the permissions (scopes) you were given.
2. Look at your tool list (`tools/list`). It shows only the tools this connection may call.
   `dtc_capabilities` adds detail, but call only tools that `tools/list` shows.
3. If a tool you need is missing, the merchant did not grant that permission. Tell them which
   one, and ask them to disconnect you in **Agents → Connected agents** and connect again with
   it ticked. Never guess, and never look for a way around it.
4. Before you write any copy, read `dtc_get_brand_system` so it sounds like their brand.

## 3. Set up a store, step by step

Ask the merchant what they sell first. Use their real products, prices, photos and words.

1. **Products.** See what is there with `dtc_list_products`. Add one with `dtc_upsert_product`,
   or many with `dtc_bulk_import_products` (a job). To resell wholesale products from other
   PlatformDTC merchants: `dtc_network_search` → `dtc_network_get_listing` →
   `dtc_network_import_listing`, only after the merchant confirms the listing, variants and
   prices. Prefer suppliers marked verified (checked by the PlatformDTC team) and quote their
   real scorecard numbers; an absent scorecard means not enough orders yet, never a rating to
   guess. Change prices, SKUs or stock with `dtc_update_product` and `dtc_bulk_update_variants`.
2. **Collections.** Group products with `dtc_create_collection` (title and `product_ids`), and set
   their order with `dtc_set_collection_products`. `dtc_list_collections` shows what exists.
3. **Storefront theme.** Follow the theme skill:
   https://platformdtc.com/skills/platformdtc-theme/SKILL.md. In short: `list_themes` →
   `create_dev_theme` → `get_theme_manifest` → `get_section_schemas` → `write_theme_files` →
   `check_theme` → `preview_theme`. Never edit the live theme directly. Show the merchant the
   preview.
4. **Publish.** Only when the merchant asks and `check_theme` passes: `publish_theme`, then follow
   `get_publish_status` until it finishes. It needs the `publish_themes` permission.
5. **Custom domain.** `dtc_add_custom_domain` with the domain and, as `project_id`, the `id` of
   the live theme (role `main` in `list_themes`). It is a job: wait for it, then give the
   merchant the DNS records to add at their domain provider. It needs `store:publish`.

Adding products and collections is propose-first. Show the merchant exactly what you will add.
After they say yes, send the same call with `approved_apply: true`. Without it, the call is
refused with a "propose-only" message. That is expected, not something to work around.

Only build a separate new storefront (`dtc_create_store`) when the merchant asks for one.

## 4. Everyday work

Good starting tools, when you have the permission:

- **Orders:** `dtc_list_orders`, `dtc_get_order`, `dtc_fulfill_order`, `dtc_list_subscriptions`
- **Customers:** `dtc_list_customers`, `dtc_create_segment`
- **Marketing:** `dtc_create_campaign` (a draft), `dtc_send_campaign`, `dtc_create_automation`,
  `dtc_activate_automation`
- **Results:** `dtc_get_dashboard_analytics`, `dtc_get_funnel`, `dtc_get_ad_insights`
- **Abandoned carts:** `dtc_list_abandoned_checkouts`, `dtc_recovery_report`
- **Money:** `dtc_get_balance`, `dtc_get_transactions`
- **Support inbox:** `dtc_list_support_tickets` (open ones first), `dtc_get_support_ticket`,
  `dtc_get_ticket_customer` (their orders, subscriptions and parcels), then `dtc_reply_to_ticket`,
  `dtc_add_ticket_note` or `dtc_update_ticket`
- **Shipping:** `dtc_list_shipments` (`lens: "exception"`, `"stalled"` or `"late"` finds parcels
  in trouble), `dtc_get_shipment` for the scan timeline, `dtc_update_tracking` to fix a number
- **Returns:** `dtc_list_returns`, `dtc_get_return`, `dtc_create_return`, `dtc_approve_return`,
  `dtc_decline_return`, `dtc_mark_return_received`
- **Subscriptions:** `dtc_get_subscription` (its `next_charge` says when the next renewal charges
  and how much), `dtc_pause_subscription`, `dtc_resume_subscription`, `dtc_skip_subscription_order`,
  `dtc_reschedule_subscription`, `dtc_change_subscription_quantity`, `dtc_swap_subscription_product`,
  `dtc_change_subscription_frequency` (plans from `dtc_list_subscription_plans`) and
  `dtc_cancel_subscription` (always with the reason the customer gave)
- **Fraud:** `dtc_list_flagged_orders`, `dtc_get_order_risk` (PlatformDTC's own risk level and the
  reasons behind it)
- **Chargebacks:** `dtc_list_disputes` (watch `evidence_due_by`), `dtc_get_dispute`,
  `dtc_get_dispute_evidence`, `dtc_submit_dispute_evidence`
- **Refunds and charges (these move money):** `dtc_refund_order`, `dtc_list_order_refunds`,
  `dtc_charge_subscription_now`, `dtc_change_subscription_price`, `dtc_cancel_order_as_fraud`

New tools are added over time, so trust `tools/list` over this list. Each tool's description
says what it needs.

### Facebook ads

Needs `ads:read` to look and `ads:write` to build. Start with `dtc_meta_connection`: it shows the
store's Facebook ad accounts, whether one needs reconnecting, and each account's **daily ad
limit**.

- **No daily limit, no spending.** If the account has none, the merchant sets it in the
  PlatformDTC dashboard. Never pick a limit for them.
- **Launching:** `dtc_meta_launch_sales_campaign` builds the campaign, the ad set (optimized for
  purchases) and one ad per image or video — all **paused** — then waits for the merchant to
  approve going live. Pass `product_id` to advertise a store product, the daily budget in the
  account's currency (`"50"` = 50.00 a day) and who should see it (`worldwide`, or `countries`).
  Write the ad text only from what the merchant told you.
- **Pausing and lowering a budget** happen at once. **Turning something on or raising a budget**
  (`dtc_meta_set_status`, `dtc_meta_set_budget`) comes back `pending_approval` with a `job_id`.
- Say budgets back in money ("$50.00 a day"), and use only the Pages marked `can_advertise`.

## 5. Stay safe

**It is real.** Every change reaches the live store. Say what you are about to do before any
change the merchant did not ask for in so many words.

**Some actions wait for the merchant.** When a result says `status: "pending_approval"`, nothing
has happened yet. There are two kinds:

- **With a `job_id`** (sending a campaign, turning on a flow, publishing, adding a domain, turning
  on Facebook ads or raising their budget, and others depending on the store's settings): the action is parked. Tell the merchant what is
  waiting and that they can approve it in **Agents → Approvals**
  (https://platformdtc.com/agents/connect?section=approvals). Do not call it again. Later,
  `dtc_job_wait` with the same `job_id` shows the result.
- **With `approval_reason` and `impact_summary`, or `summary` and `impact`** (fulfilling an order,
  starting an A/B test, applying a winning price, refunds and the other money actions): explain
  the impact and ask. Only after the merchant says yes to that exact action, call it again with
  `approval_context: {"merchant_confirmed": true}`.

**Jobs.** Some tools return `{job_id, status}` instead of a result. Call `dtc_job_wait` with that
`job_id` until the status is `succeeded`, `failed` or `cancelled`. If it comes back with
`wait_complete: false`, call it again. Never say a job is done before it is.

**Retries are safe.** The server adds an idempotency key to every change, so repeating the same
call never does it twice. Do not pass `idempotency_key` yourself.

**Never guess permissions.** A missing tool, `insufficient_scope` or `FORBIDDEN_SCOPE` means the
merchant did not grant it. Stop and tell them which permission you need.

**Errors** come back as tool results with `isError: true` and a code. Read the message and fix
the call. Only retry unchanged after a rate limit or a server error.

**Replying to customers.** A reply goes to a real customer at once and can't be taken back.
Read the whole ticket and `dtc_get_ticket_customer` first, and answer only from what you can
see. Don't promise refunds, discounts or delivery dates. When you are not sure, leave an
internal note with `dtc_add_ticket_note` and ask the merchant instead. `dtc_update_tracking`
emails the customer only if you set `notify_customer: true`, so ask first. Returns never move
money by themselves: a refund is a separate step (`dtc_refund_order`) the merchant approves.

**Money.** Refunds, charging a subscription now, changing a subscription's price and cancelling
an order as fraud move real money. Call the tool once without `approval_context`: nothing moves,
and you get a `summary` and an `impact` (the exact amount, the order or subscription, what else
happens). Tell the merchant in plain words and ask. Only after they say yes to that exact action,
call it again with `approval_context: {"merchant_confirmed": true}`. If the answer has a `job_id`,
the merchant approves it in **Agents → Approvals** instead; wait with `dtc_job_wait`. Never refund,
charge or cancel as fraud on your own judgement. Quote amounts from `impact` or `next_charge`,
never from your own math. Chargeback evidence: read what is already on file first
(`dtc_get_dispute_evidence`), save yours as a draft (`submit: false`), use only facts from the
order (tracking, delivery date, what the customer bought, the store's policies), and send it
(`submit: true`) the same way as money: the first call only saves the draft and returns a
`summary`; send it again with `approval_context: {"merchant_confirmed": true}` only after the
merchant says yes. The bank gets it once and it can't be changed.

**Nothing made up.** No placeholder text, fake reviews, ratings, stock counts or invented claims.
If you do not know a value, leave it out and ask the merchant. Contact customers only when they
have agreed to hear from the store and there is a real reason.

## 6. When you finish

Tell the merchant:

- what changed, with links (preview or live URL);
- what is waiting for their approval, and where to approve it;
- anything left to do, and anything you need from them.
