---
title: "One form for any company. Returns 201 with your API key once (we keep only a hash). Providers say what service they are so leads reach them."
method: POST
path: "/v1/partners"
tags: ["core"]
---

# One form for any company. Returns 201 with your API key once (we keep only a hash). Providers say what service they are so leads reach them.

`POST /v1/partners`

## Request

- Base URL: `https://www.putroots.com`
- URL: `https://www.putroots.com/v1/partners`
- Auth: none required

Register once. kind=partner if you send us users; kind=provider if you take leads / orders / tasks from us (then service and at least one delivery are required). service is one of movers | storage | cleaning | furniture | grocery | insurance | isp | mobile, and stages must include the stage that service belongs to (move_planning / settle / activate). Affiliate providers (insurers, ISPs, carriers, stores): delivery.affiliate_code is your full https:// tracking link; config.vendor (one of the vendor names our task graph uses) is required when it is your only delivery and lets you omit service (we infer it). Responds 201; status is pending until approved by email within 1 business day (unless auto-approval is on).

## Request body

- object
  - `code` string, required: Your short code: 3–32 lowercase letters / digits / hyphens. Used in hand-off links and outbound links.
  - `name` string, required
  - `kind` 'partner' | 'provider', required: partner = you send us users and watch their stage progress; provider = you take tasks from us (leads, orders).
  - `stages` string[], required: Which journey stages you are in. Scopes are derived from them.
  - `contact_email` string, email, required
  - `service` 'movers' | 'storage' | 'cleaning' | 'furniture' | 'grocery' | 'insurance' | 'isp' | 'mobile': Required for kind=provider (partners omit it): what kind of provider you are. movers / storage / cleaning need stage move_planning; furniture / grocery need settle; insurance / isp / mobile need activate. Affiliate providers may omit it when config.vendor is set: we infer it from the vendor. Puts you in the directory so leads / tasks reach you.
  - `delivery` object: provider only: how we deliver to you. At least one.
    - `endpoint` string, uri: We POST { event: "lead.created", lead } here, signed with X-PutRoots-Signature (HMAC-SHA256). Additive to intake_email for quote services.
    - `intake_email` string, email: Required for movers / storage / cleaning: the renter's quote request is emailed here from their own mailbox; the JSON lead follows to endpoint if set.
    - `affiliate_code` string, uri: Your full https:// affiliate tracking link, as issued by your affiliate network (not a bare code). When our agent does a task on your site for a user it opens this link as the entry (the 302 lands on your page inside our session), and records a link_out event you can read from GET /v1/events. Requires config.vendor when it is your only delivery. Not for Instacart (attribution is on their API key).
  - `config` object: Adapter-level settings for your company only; the toolkit itself is the same for everyone.
    - `vendor` 'lemonade' | 'mint-mobile' | 'us-mobile' | 'visible' | 'sonic' | 'att-fiber' | 'xfinity' | 'astound' | 'ikea' | 'weee' | 'instacart': The vendor name our task graph knows you by. Required when delivery.affiliate_code is your only delivery; it is how a link_out is attributed to you. If you set it, service may be omitted (inferred). Not listed? Tell us and we add it.
  - `test` boolean: Issue a pr_test_ sandbox key instead of a pr_live_ one. A sandbox key works immediately, with no approval step, and runs dry: leads come back redacted, real leads are never delivered to you, nothing is recorded, hand-offs are not billable. A code's key type is fixed at registration and a code can only be registered once, so register your sandbox under its own code (e.g. acme-sandbox) and your production code separately.

## Response `201`

Created

- object
  - `source` unknown, required
  - `fetched_at` string, date-time, required
  - `ok` boolean, required
  - `code` string, required
  - `kind` string, required
  - `stages` string[], required
  - `scopes` string[], required
  - `status` 'pending' | 'active', required
  - `api_key` string, required: Shown once. Store it; we keep only a hash.
  - `webhook_secret` string: Providers with a delivery.endpoint only. Shown once. We sign every POST to your endpoint: X-PutRoots-Signature: sha256=<hex(HMAC-SHA256(webhook_secret, raw request body))>.
  - `note` string: status=pending: approval by email within 1 business day; the key works once approved. status=active: the key (and webhook_secret) are shown once.

## Other responses

- `400`: Bad input — error says which field.
- `401`: No key / bad token.
- `403`: Key is fine but not for this: pending approval, revoked, scope missing, or an operation only the renter's own account may call.
- `404`: No such lead / hand-off / listing under this key.
- `409`: Conflict: the code is already registered.
- `429`: Rate limited; Retry-After in seconds.

---

[API](https://skmtc.dev/putroots/apis/putroots-api.md) · [All operations](https://skmtc.dev/putroots/apis/putroots-api/llms.txt) · [OpenAPI document](https://skmtc.dev/putroots/apis/putroots-api/revisions/a859d719878d?raw)
