PitchPrfct
First-party SMS/CRM integration connected via API key — powers the dialer SMS tab and lands as the first entry in Project88's typed provider registry.
PitchPrfct is the first integration to land on Project88's typed
provider registry — a folder-per-provider layout under
src/integrations/ that drives a new Project 88 tab in the
Connections settings modal, alongside the existing Integrations
(Composio-managed) and Tools tabs.
The PitchPrfct entry covers all 46 endpoints across 8 resource groups
through a single allowlisted path-passthrough edge function
(pitchprfct-api), and it powers the dial widget's
SMS tab.
Connect
⌘K→ Settings → Connections → Project 88 tab.- Click Connect on the PitchPrfct card. The generic
ApiKeyConnectModalopens — it matches the project's condensedConfirmDialogstyling and runs a per-providervalidate()hook before persisting. - Paste your PitchPrfct API key and submit. The key transits the
pitchprfct-apiedge function exactly once (with avalidateKeyenvelope) to confirm it's live, then lands in Supabase Vault via the discriminator-shapedinsert_integration_api_keyRPC. The key never reaches the browser again after connect.
You'll see PitchPrfct in the Project 88 tab with a "Connected" status.
Where the credential lives
| Stored | Notes |
|---|---|
Supabase Vault, via insert_integration_api_key (migration 126) | Payload is the self-describing discriminator { type: 'api_key', key, provider } — distinct from the OAuth-shaped payload used by Gmail / Outlook / Google Calendar |
RPC permissions (migration 127) | REVOKE EXECUTE FROM anon; GRANT EXECUTE TO authenticated. Clears Supabase advisor lint 0028 |
| Edge function in-memory cache | Map<userId, key> with a 5-min TTL, scoped per warm instance |
The proxy never echoes the key back to the browser, and only allowlisted PitchPrfct paths can be reached through it.
Edge function: allowlisted path-passthrough
supabase/functions/pitchprfct-api is a single proxy with
verify_jwt: true. One regex covers all 46 endpoints across the 8
resource groups, and a 26-scenario Deno suite asserts the allowlist
plus a leak canary (the key must never appear in the response body or
in proxied error envelopes).
The browser-side typed client (src/integrations/pitchprfct/client.js)
is a hand-rolled JSDoc-typed JS wrapper (no TS in this codebase) with a
generic pollJob() helper for the bulk endpoints.
Drift guard
A nightly contract test under .github/workflows/contract-tests.yml
runs three live checks against PitchPrfct using the
PITCHPRFCT_TEST_API_KEY GitHub secret. If PitchPrfct ships a breaking
change, the workflow fails before the next user opens the dial widget.
Known upstream quirk
PitchPrfct's conversations.list({contactUuid}) endpoint returns
HTTP 500 for valid UUIDs. The dialer SMS tab works around it by
skipping the conversations endpoint and calling
messages.list({contactUuid}) directly — a comment at the call site
flags the workaround so it can be unwound once upstream fixes the bug.
Sending: fromNumber requirement
PitchPrfct exposes no API to enumerate sending numbers, so outbound
sends read from localStorage['dial.sms.pitchprfct.fromNumber']. If
the value is missing, the message bubble flips to failed with a
clear configuration error rather than dropping silently. A
first-party settings UI for the from-number is a follow-up.
Adding another Project 88 integration
The registry layout is intentionally copy-paste-able. To wire a new first-party provider:
- Duplicate
src/integrations/pitchprfct/tosrc/integrations/<new-provider>/. - Swap the provider definition in
index.js(display name, icon,validate()hook, base URL). - Update the typed client wrapper and its tests.
- Add the new provider to
src/integrations/registry.js.
The same vault path, RPC, modal, and Project 88 tab pick it up automatically.
Webhooks
Inbound PitchPrfct webhooks are deferred — v1 ships on-demand only. The dial widget polls every 5 seconds for new inbound SMS while the conversation is open (paused during an in-flight optimistic send so the poll can't clobber it); nothing pushes into Project 88 yet. A webhook lane will land when user behavior calls for it — at which point the poll drops out.
Where to next
- Dial → SMS tab — where this integration is consumed
- Vault and secrets — how Project88 stores credentials in general
- Webhooks — when the webhook lane ships