---
name: super-communications-sdk
description: >
  The typed client for Super's platform API and integration gateway, and
  the builder toolkit that goes with it. Reach for this SDK when the user
  asks to build a Super integration, to write automations against HubSpot
  or Buildium, to sync data between property management and CRM, to
  publish events into Super's trigger system, to register a webhook
  subscription (`createWebhookSubscription`), to define a trigger
  (`createTrigger`), to configure an agent and bind it to a trigger
  (`createAgent`), to read invocations (`getInvocation` /
  `iterateInvocations`), to deploy a webhook consumer
  or scheduled sync (render.com is the recommended host), to verify
  Super's webhook signatures, or when `@super-communications/sdk` is a
  dep in the project. One client, three connections — never build URLs,
  never hold provider credentials, never hand-roll retries. See
  BOOTSTRAP.md for zero-to-a-working-integration, RECIPES.md for
  intent-organized recipes, DEPLOYMENT.md for the render.com deployment
  story, PROMPTS.md for mechanic-organized patterns.
allowed-tools: Read, Grep, Glob, Edit, Bash
---

# super-communications-sdk

Reference for `@super-communications/sdk` — the typed client for Super's
platform API and integration gateway. This skill loads when the package
is a dep in the current project; consult its content **only when the
user's intent is Super-related** (building a Super integration, syncing
HubSpot ↔ Buildium via Super, publishing a Super event, responding to a
Super webhook). If the user is doing unrelated work in a project that
happens to include this SDK, don't nudge them toward SDK calls they
didn't ask for. Companion file: `PROMPTS.md` alongside this one has
canonical worked prompts.

## What the SDK is

One client (`SuperClient`) with three connections:

- **`core`** — Super's own platform API. Configure agents, create/list
  triggers, bind agents to triggers (via `createAgent`'s `agentTrigger`
  field), publish events, create/list/update/delete webhook
  subscriptions, read invocations (get one / list / iterate). Everything
  a Super integration needs to configure itself and observe what
  happened, end-to-end via API. Authenticated with the `sk_super_` key
  directly. No vendor proxy involved.
- **`hubspot`** — the company's connected HubSpot portal, reached through
  Super's integration gateway. Super injects the HubSpot credentials on
  each call; callers never see a HubSpot token. Requires the
  `integrations:hubspot:proxy` scope on the API key.
- **`buildium`** — the company's connected Buildium account, same gateway
  pattern. 38 resource namespaces (leases, workorders, rentalUnits,
  applicants, ...) each with typed methods. Requires
  `integrations:buildium:proxy`.

## Intent → Reach for (the translation table)

When a user's ask is business-shaped, this is where to start. Full worked
recipes live in `RECIPES.md`; this table is the fast lookup.

| The user's intent                       | Reach for                                                                                                                                                                                                                                                          |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Walk every record matching a filter     | `client.<vendor>.<resource>.iterate<Noun>` — paginates transparently                                                                                                                                                                                               |
| Read one record by id                   | `client.<vendor>.<resource>.get<Noun>ById`                                                                                                                                                                                                                         |
| Fire a Super workflow / trigger         | `client.core.publishEvent({ event, data, metadata })`                                                                                                                                                                                                              |
| Register a new webhook endpoint         | `client.core.createWebhookSubscription({ url, events })` — the response carries the `whsec_…` signing secret ONCE; capture it in the same operation. Requires `webhooks:write`.                                                                                    |
| List / update / delete a webhook        | `client.core.listWebhookSubscriptions()`, `updateWebhookSubscription({ subscriptionId, url?, events? })`, `deleteWebhookSubscription({ subscriptionId })`                                                                                                          |
| Create a new trigger definition         | `client.core.createTrigger({ name, eventName, variables, phoneVariable?, emailVariable? })` — requires `triggers:write`. Defines the event schema. Capture `trigger.id` for the agent-binding call.                                                                |
| Create + bind an agent                  | `client.core.createAgent({ name, direction, voiceSettings, dialOutSettings, agentTrigger: { type: 'WEBHOOK', triggerId } })` — requires `agents:write`. One call configures the agent and binds it to a trigger. Outbound-`CALL` agents require `dialOutSettings`. |
| Read a single invocation by id          | `client.core.getInvocation({ invocationId })` — the `invocationId` you get back from `publishEvent`. Requires `invocations:read`.                                                                                                                                  |
| Walk every invocation matching a filter | `client.core.iterateInvocations({ query: { status? } })` — paginates transparently via the `startingAfter` cursor.                                                                                                                                                 |
| Update a HubSpot contact                | `client.hubspot.updateContact({ contactId, properties })`                                                                                                                                                                                                          |
| Create a HubSpot task on a contact      | `client.hubspot.createTask({ subject, body, contactId })`                                                                                                                                                                                                          |
| Find charges N days late on a lease     | `client.buildium.leaseTransactions.getAllCharges({ leaseId, query: { transactiondateto } })` — day-precise via `LeaseCharge.Date`.                                                                                                                                 |
| Get an aged-bucket balance summary      | `client.buildium.leaseTransactions.getLeaseOutstandingBalances({ query: { leaseids } })` — per-lease `LeaseOutstandingBalance` in 30-day buckets; single-lease lookup passes `query.leaseids: [id]`.                                                               |
| Create a Buildium work order            | `client.buildium.workOrders.createWorkOrder({ body })` — required: `EntryAllowed`, `VendorId`.                                                                                                                                                                     |
| Consume a Super webhook                 | see `DEPLOYMENT.md` § Webhook consumer + `verifySuperSignature`                                                                                                                                                                                                    |
| Run something on a schedule             | see `DEPLOYMENT.md` § Cron worker                                                                                                                                                                                                                                  |
| Backfill / one-off migration            | see `DEPLOYMENT.md` § One-shot script                                                                                                                                                                                                                              |
| Handle a missing proxy scope            | catch `MissingScopeError`; remediation is Settings → API Keys                                                                                                                                                                                                      |
| Handle a customer disconnect            | catch `IntegrationNotConnectedError` vs `CredentialsUnavailableError`                                                                                                                                                                                              |

If the ask doesn't fit any row, the shape is probably compose-across-connections — check `RECIPES.md` for the closest recipe and adapt.

## The shape of a good build

**Construct once, share.** `new SuperClient({ apiKey, baseUrl })` at the
edge of the app (module scope, DI container, worker startup). Not per
request. Timeouts + observability hooks + connection-specific overrides
all go into the constructor.

**Reach for the right connection.**

- Writing to HubSpot? `client.hubspot.updateContact({ ... })`.
- Reading from Buildium? `client.buildium.<resource>.get<Whatever>({ ... })`.
- Publishing a Super event that fires a trigger? `client.core.publishEvent({ ... })`.

**Type-first authoring.** Every method takes an `input: { ... }` object.
Path params are typed (`leaseId: string | number`), query filters are
typed (`query?: GetLeasesQuery`), create/update bodies are typed
(`body: LeaseCreate` / `body: LeaseUpdate`). Import Buildium entity types
via the `Buildium` namespace: `import type { Buildium } from
'@super-communications/sdk'` then `Buildium.Lease`, `Buildium.LeaseCreate`,
`Buildium.GetLeasesQuery`, etc.

**Error handling — narrow by class, not by string.** Every failure throws
a subclass of `SuperSdkError`. Catch the right one:

- `MissingScopeError` — key exists but doesn't carry the scope this call needs. `error.requiredScope` names it (e.g. `integrations:buildium:proxy`); remediation is Settings → API Keys.
- `IntegrationNotConnectedError` — customer hasn't linked the vendor. Prompt them to connect.
- `CredentialsUnavailableError` — link exists but the vendor rejected. Prompt reauth.
- `BuildiumUpstreamError` — Buildium rejected the call. `error.upstream` is a typed `BuildiumErrorBody` (has `UserMessage`, `ErrorCode`, `Errors[]`).
- `HubspotUpstreamError` — HubSpot rejected. `error.upstream` is a typed `HubspotErrorBody` (has `category`, `message`, `correlationId`).
- `RateLimitError` — 429. The SDK backs off on idempotent methods automatically; POST/PATCH raise this immediately.
- `UpstreamError` — any other vendor. `error.upstream` is `unknown` here.
- `SuperSdkError` — base class if you want to catch everything.

**Retries.** Idempotent methods (GET/HEAD/PUT/DELETE) retry twice on
429/5xx/network with exponential backoff + honored `Retry-After`. POST and
PATCH never retry automatically — a duplicated write can't be undone.
Handle those failures explicitly.

**Cancellation + timeouts.** Every method accepts a second `options?: RequestOptions`
argument: `{ signal, timeoutMs, correlationId }`. Wire an `AbortSignal`
through for long-running iterators. Per-connection defaults live on the
constructor's `connectionTimeouts` (`buildium` iterators can legitimately
need 60s+).

## Non-goals — DON'T do these

- **Don't call HubSpot or Buildium directly.** Their credentials aren't
  yours to hold; use the SDK, which routes through Super's gateway with
  Super's API key.
- **Don't paginate by hand.** Every paged Buildium GET (any `getAll*` /
  `get<Noun>s` with `offset`/`limit` query params) has an auto-generated
  `iterate<Noun>` companion that walks pages transparently. Use `for await
(const x of client.buildium.leases.iterateLeases({ query })) { ... }` —
  the iterator honors `iterOptions.signal` between pages.
- **Don't `catch (error)` and `error.message`.** Narrow by class, read
  `error.code` for the stable machine-facing identifier, read
  `error.requestId` for support hand-off.
- **Don't guess method names.** All 38 Buildium namespace files are
  generated from Buildium's OpenAPI spec — every method mirrors an
  operationId. `NAMESPACES.md` in this package is the flat inventory
  (method → path → entity → query → iterator per namespace); read it
  before greping. Autocomplete/hover works in an editor with LSP.
- **Don't build a `SuperClient` per request.** Retry state,
  connection-timeout config, and observability hooks all live on the
  client. Construct once at the edge; share the instance.
- **Don't retry POST/PATCH manually.** The SDK deliberately doesn't
  retry non-idempotent methods (a duplicated write can't be undone).
  Catch `RateLimitError` and decide — queue for later or fail loud.
- **Don't ignore `error.requestId`.** It's the single token for support
  hand-off. Log it on every terminal failure alongside `error.code`.
- **Don't parse webhook bodies before verifying signatures.** Signature
  is computed over the raw request bytes; JSON-parsing them first breaks
  verification. Use raw-body middleware. See `DEPLOYMENT.md` §
  Signature verification for the tested snippet.
- **Don't grant `integrations:*:proxy` broadly.** Narrower keys are
  safer if leaked. Check the proxy checkbox at key creation only when
  the app using this key actually calls the vendor.

## When to reach outside the SDK

The SDK covers the full integration lifecycle end-to-end. Two touchpoints
sit in the admin app because they need a browser round-trip:

- **Vendor OAuth linking (Buildium, HubSpot).** OAuth needs a browser.
  **Settings → Integrations**, once per vendor.
- **First display of a new `sk_super_…` API key.** The value is shown
  once, at creation, in the admin app. A key with the write scope needed
  to mint another key would be `sudo su`-shaped, so key minting stays
  browser-gated by design.

The `whsec_…` webhook signing secret is returned by
`client.core.createWebhookSubscription` in the API response — one-shot;
list and read return the subscription without the secret. Capture it
in the same operation that made the call.

Beyond those two OAuth-shaped touchpoints, if a Super capability seems
missing from the SDK, check `NAMESPACES.md` and this file's intent
table first — the answer is more often a method the caller hasn't
spotted yet than an actual gap.

### Reserved (declared, not shipped)

- **`TriggerSource: 'INTEGRATION'`** — the enum value is declared but no
  vendor-direct ingestion path is wired today. Practically, use
  `WEBHOOK` and call `publishEvent` from your own bridge that receives
  the vendor's webhook. Do not build against `INTEGRATION` yet.
- **Per-invocation scheduling** — there is no
  `publishEvent({ scheduledFor })` or `scheduleInvocation` primitive. If
  you need "run this specific call at time T," schedule it on your own
  side (cron / queue / Temporal) and call `publishEvent` at the right
  moment. `TriggerSource: 'CRON'` is a _recurring event source_, not
  per-invocation scheduling.

For non-Super gaps: Buildium coverage is derived from a server-side
allowlist. Expanding it means growing the allowlist first, not
hand-writing a call.

## Reference

- **Zero to a working integration** — `BOOTSTRAP.md` is the front-to-back
  setup sequence for someone starting from nothing: link the vendor,
  mint a scoped key, create a trigger via the SDK, register a webhook
  via the SDK, write the runtime code. Reach for this when the user
  says "I want to build a Super integration" and hasn't done setup yet.
- **First-time setup (single-call check)** — `GETTING_STARTED.md` is the
  zero-to-first-successful-call walkthrough (create the key, install,
  first `core.publishEvent`, first proxy call). Reach for this when the
  user has the pieces set up and just wants to confirm the client works.
- **Business-language recipes** — `RECIPES.md` is the intent-organized
  cookbook. Each recipe leads with a business ask and translates to
  primitives. Reach for this when the user's ask is business-shaped
  ("every morning at 6am, sync overdue leases into HubSpot tasks…").
- **Deployment** — `DEPLOYMENT.md` covers the render.com deployment
  story: webhook consumer, cron worker, one-shot script. Signature
  verification snippet lives here (tested end-to-end). Reach for this
  when the user asks about hosting, cron scheduling, webhook endpoints,
  or signature verification.
- **Patterns** — `PROMPTS.md` is the mechanic-organized companion
  (construct, read, list, paginate, create, compose, publish, narrow
  errors). Reach for this when the user already knows the shape and
  wants the canonical pattern.
- **Buildium method inventory** — `NAMESPACES.md` lists every method
  across all 38 Buildium namespaces (method → HTTP → path → entity type
  → query type → iterator). Reach for this before greping when the user
  says "does the SDK have a method for X?" — one file read collapses
  what would otherwise be one grep per guess.
- **API reference** — the published SDK is at
  `@super-communications/sdk` on npm; install via
  `pnpm add @super-communications/sdk`. Every method has full JSDoc
  with `@param`, `@returns`, and `@throws` — hover in your editor to
  see it. High-traffic methods also carry `@example` blocks referencing
  a recipe by name.
- **Overview** — `README.md` is the human-authored intro + Usage +
  Errors + Retries + Cancellation sections.
