# Connect your product

## Configure your source and a token

Create a source for your application in Account → API and exports → *Id sources* (for example
name Kitchnware, API name `kitchnware`). The API name is yours and stays fixed. Create a read-and-write
token for the integration. A token is not tied to a source: the address of each call says whose id it uses.

One id is one customer. A customer can hold several ids of your source: a company with two workspaces
is one customer, and its usage is the sum of both.

## Send each organization, with who pays

The libraries send it for you ([`gem "sumwerk"`, `npm install sumwerk`](/docs/api#libraries)):
`sumwerk.customers.upsert("kitchnware", org.id, name:, email:)` in Ruby and
`sumwerk.customers.upsert("kitchnware", org.id, { name, email })` in TypeScript make the call below.

```console
PUT /api/v1/sources/kitchnware/customers/org_123
{"name":"Example Company","trial_started_at":"2026-09-24","stripe_customer_ids":["cus_Q2x…"],
 "custom":{"company_type":"agency"}}
```

1. Check `GET /api/v1/me` for the expected account and environment when the integration starts.
2. Send every organization once a day, and when something changes. Only supplied fields change;
   `custom` keys merge, null removes a key.
3. Send the organization's Stripe customer in `stripe_customer_ids`. Stripe is the truth: a new
   organization whose Stripe customer sumwerk already holds joins that customer instead of becoming a
   duplicate. A Stripe customer that belongs to another customer is never moved; the answer carries
   `warnings` and a person merges the two in sumwerk if they are one company.
4. Keep the returned `id` if you want to link to the customer in sumwerk.
5. Send the organization's people to `POST /api/v1/sources/kitchnware/customers/org_123/contacts`.

An organization that has not paid yet and that sumwerk only knows from somewhere else (a ChartMogul lead,
a booked call) can be attached first: find it (`GET /api/v1/sources/chartmogul/customers/<uuid>`), then
`POST /api/v1/customers/<id>/external-references` with `{"source":"kitchnware","external_id":"org_123"}`.
A retry of the same link is safe; an id that belongs to another customer returns `409 identity_conflict`.

## Send usage every day

```console
PUT /api/v1/sources/kitchnware/usage/2026-09-23
{"customers":{"org_123":{"ads_launched":12,"active_users":3},"org_124":{"ads_launched":1}}}
```

One call per day for all organizations; leave out those that did nothing. Send the last three days
every night, so late data is corrected, and your history once as a backfill. Describe the metrics on
the Attributes page (name, unit, sum or level) and build your at-risk rules on their windows, for
example `usage.ads_launched.change_28d_pct`. Details in the [API reference](/docs/api#usage-over-time).

What your application sends is its own: an import from ChartMogul never overwrites those attributes,
nor the name, company and trial dates of your organizations.

## Ask what each organization may do (Billing)

With Billing set up ([Set up billing](/docs/billing-setup)), your application asks sumwerk what each
organization may do, under the same source and ids:

```console
GET /api/v1/billing/kitchnware/orgs/org_123
POST /api/v1/billing/kitchnware/usage
{"events":[{"event_id":"use_8842","org":"org_123","feature":"exports","quantity":1,"occurred_at":"2026-10-09T07:58:02Z"}]}
```

The answer names the plan, every feature, limit and rollout flag, and what was used. Changes come to
your webhook (Billing → Settings → Connection) or through `GET /api/v1/billing/kitchnware?after_version=`.
Each `event_id` counts once, so a batch can be sent again safely. The clients do all of this and keep the
answers in your own store: `entitled?`, `allowed?`, `track`, `report`. Fields, errors and what to retry:
[API reference](/docs/api#billing-what-each-customer-may-do).

## Update existing integrations

The old unscoped customer PUT returns 410. Customer responses no longer contain unlabelled
`external_id`/`external_ids` or ignored-write/primary/retired fields. Contact/note/task external ids
remain supported for their own retries. See the [API reference](/docs/api) for fields and errors. Retry
transient failures with bounded backoff and respect `Retry-After`; authentication, validation and
mapping conflicts need correction.
