Overview

Settings → Connectors at /settings/connectors is the operator-facing console for the inbound write half of the API. It's where you mint, watch, and revoke the pck_int_* keys that external storefronts (WooCommerce, Shopify, Zapier, Make, your own backend) use to push orders, payments, and clients into {{COMPANY_SHORT_NAME}}.

Connectors is the UI-only counterpart to the Integration API reference. Everything you can do from this page is also doable from core.rpc_*_integration_key* SDFs, but this is how you'll use it day-to-day.

What working correctly looks like

If your connector is wired up, you should see all of these. If any one is missing, jump to Troubleshooting.

  • Each external system you connect shows up as one row in the Active connections table.
  • The row's Last used column ticks forward within seconds of the upstream system POSTing.
  • The 24h requests badge climbs as traffic flows; the errors sub-count stays at 0.
  • Clicking a row opens the Request log drawer with one entry per POST — status 2xx, latency under ~300 ms.
  • Orders created via the connector show an imported · <source> chip in the operator orders list.
  • Revoking a key takes effect immediately — the next POST returns 401 invalid_api_key.

Setup

You'll need org admin role to open the page. Non-admins see a read-only restricted-access card. Workspace owners are admins by default; promote teammates from Settings → Team.

You'll also want at least one store created (Settings → Stores) before minting a key — every integration key is permanently pinned to one (workspace, store) pair. There is no “write to any store” key; that's a deliberate isolation choice for multi-store operators.

Quickstart cards

On first load (and any visit thereafter), the top of the page shows three quickstart cards that preset the “New integration key” modal:

  • WooCommerce — preset source woocommerce, payload format woocommerce, default name “WooCommerce”. Forces require_signature = true (the DB check constraint won't accept a WC key without it).
  • Zapier / Make — preset source zapier, payload format peptideclients, default name “Zapier”. Signature optional but recommended.
  • Custom integration — preset source custom, payload format peptideclients. Use this for your own Node/Python/cURL code, Squarespace, Wix, Webflow, or anything not in the catalog above.

The cards are convenience defaults; every field on the modal stays editable.

New integration key modal

Whichever quickstart card you pick, you'll land on the same modal. It walks you through six fields:

  1. Store — the key is permanently pinned to the picked store. There is no “any store” option. Pick the store whose orders the upstream system is going to create.
  2. Name — an internal label (e.g. “Production WooCommerce”, “Zapier — Square orders”). Only you see it. Pre-filled from the quickstart card.
  3. Source label — one of custom, woocommerce, zapier, make, shopify, square, bigcommerce, stripe_checkout, squarespace, wix, webflow, other. Stamps the imported · <source> chip on every order this key creates.
  4. Scopes — pick only what the integration needs:
    • orders:write — create orders
    • orders:cancel — cancel orders
    • payments:write — record payments
    • payments:refund — refund payments
    • clients:write — upsert clients without an order
  5. Rate limit per minute — defaults to 120. Range 1–10,000. The platform-wide ceiling is 6,000 /min (see Integration API → Rate limits); raise it later if traffic warrants.
  6. Expires in (days) — optional. Range 1–3650. Leave blank for “no expiry”. We recommend a 90-day expiry for production keys and a 30-day expiry for testing keys.

One toggle, below the fields:

  • Require signature — defaults to ON. Forces every POST to carry an HMAC of the body. Cannot be turned off for WooCommerce keys (the DB rejects it). For peptideclients-format keys, you can flip it off if you're testing from cURL without HMAC; turn it back on before you put the key into production.

One-time secret reveal screen

Hit Create and we move to a modal you'll never see again. Copy everything you see before closing it.

The screen shows two things:

  • API key — the plaintext pck_int_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx value. We store only a SHA-256 hash; we cannot redisplay it. The Copy button next to the field is the only chance.
  • Signing secret — the hex string you'll HMAC every request body against. Same one-time deal — only the plaintext lets us re-verify HMACs, but only the operator gets to see it (the secret lives in a bytea column that only the service-role context can read; the browser session never can).
If you close before copying

There is no recovery path. Revoke the key (one click on the connections table) and create a new one. The data already created against the old key stays where it is — revoke is forward-only.

Two paste-and-go follow-ups appear below the secrets:

  • For WooCommerce keys: the webhook URL (https://api.peptideclients.com/integrations/v1/webhook/woocommerce) and the signing secret pre-formatted to paste into WC's WooCommerce → Settings → Advanced → Webhooks → Secret field.
  • For peptideclients-format keys: a ready-to-run cURL command that posts a sample order with the new key and an Idempotency-Key header.

Active connections table

Every key you've ever minted (active, revoked, or expired) shows up in the Active connections table below the quickstart cards. Columns:

  • Name — the internal label you gave it, plus the first few chars of the pck_int_ prefix. Clicking the name opens the request log drawer.
  • Store — the store this key is pinned to.
  • Source — the source label (e.g. woocommerce) stacked over the payload format (peptideclients or woocommerce).
  • Scopes — chips for each scope this key holds.
  • 24h requests — live count, refreshed every 30 seconds. If the 24h error count is >0, a red {n} errors sub-badge appears below the total.
  • Last used — the timestamp of the last successful authentication. Never if the key has never been used.
  • Statusactive (olive), expired (clay), or revoked (clay).
  • Action — a Revoke button. Hidden once the key is already revoked.

Per-key stats

The 24h requests cell on each row is loaded on demand by a separate query (so the table renders instantly even with many keys). The numbers come from core.integration_request_log — the same row store the request-log drawer reads — aggregated for the last 24 hours.

Larger windows (7d, 30d) are visible from the request log drawer, which preserves the full request history per key.

Request log drawer

Click any key's Name cell to open the request log drawer on the right. It lists the most recent 50 requests against that key in reverse-chronological order. Every API call this key has authenticated lands here within a second, success or fail.

Columns:

  • Received — client-local timestamp.
  • MethodPOST, GET, etc.
  • Path — the route hit (e.g. /integrations/v1/orders).
  • Status — the HTTP status (color-coded: olive 2xx, clay 4xx/5xx).
  • Latency — milliseconds from request received to response written.
  • Idempotency key — the Idempotency-Key header value if present.
  • External id — the external_id field in the body if present.
  • Internal id / error — on success, a link to the created order. On failure, the error_code + optional detail string (rendered in clay).

The drawer is the single best place to debug a misbehaving integration — you can see exactly what the upstream system is sending, what we did with it, and (when it fails) why. Counterpart symptom-to-fix lookup table lives at Troubleshooting → Integration API.

Rotating and revoking

There is no edit-in-place for keys. Once you've minted a key, the only operation you can perform on it is revoke. That's deliberate — rotating a secret should be a deliberate, audited act, and editing scopes or rate limits on a live key would let an attacker who exfiltrated the plaintext quietly escalate.

The rotation workflow:

  1. Create a new key with the desired scopes / rate limit / source label.
  2. Paste the new pck_int_* (and signing secret if you toggled require-signature) into the upstream system's config.
  3. Confirm the upstream system has switched over by watching the Last used timestamp on the new key tick forward.
  4. Click Revoke on the old key. The confirm dialog quotes the key's name; the revoke takes effect immediately.
Recovery from a leaked key

If a pck_int_* ever lands somewhere it shouldn't (committed to git, pasted in Slack, screenshotted in support, etc.), revoke it first, then rotate. The revoke takes effect on the very next request; no need to coordinate with the upstream system.

Settings & permissions

  • Where: Settings → Connectors in the operator app (/settings/connectors).
  • Who can open it: org owners and org admins. Non-admins land on a restricted-access card with no controls.
  • Who can create keys: same — admins and owners.
  • Who can revoke keys: same.
  • Audit: every create + revoke writes a row to core.audit_log with kind = 'integration_key.created' or 'integration_key.revoked' and the operator's user id; surfacing in Settings → Audit.
  • Key scope: pinned to one (workspace, store) pair forever. No re-binding.
  • Rate limit ceiling: 6,000 req/min per key. Default 120. Configurable per-key at create time (1–10,000 in the UI, capped at 6,000 server-side).

API + automation

Everything this page does is also exposed as a callable SDF, in case you want to provision keys from Terraform / Pulumi / your own admin script:

  • core.rpc_create_integration_key(...) — mints a key, returns plaintext value + signing secret once.
  • core.rpc_list_integration_keys(...) — lists keys in the org.
  • core.rpc_revoke_integration_key(p_key_id) — revoke.
  • core.rpc_get_integration_key_stats(p_key_id) — 24h / 7d / 30d counts.
  • core.rpc_list_integration_requests(...) — the request log drawer's backing query (keyset paginated).

Each is documented in the OpenAPI spec. Sample bodies live in the recipes cookbook.

Troubleshooting

The full symptom-to-fix table lives at Troubleshooting → Integration API. Quick links:

If the request log drawer is empty even though the upstream system claims to be POSTing, the request never authenticated — double-check the bearer header value and the base URL (https://api.peptideclients.com in production, no trailing slash). If you need help, copy the row from the request log (or screenshot the empty drawer) and email {{CONTACT_EMAIL}}.