Core
Types, `ConnectError`, and pure helpers. Isomorphic — no React, no Node-only code.
Generated from @cosmoops/connect-sdk's TypeScript source and its own doc comments - see Reference for the other entry points. Regenerated on every deploy; don't hand-edit.
Classes
Class: ConnectError
Every failure the Connect application API can report, as one class.
The API speaks two error dialects and this collapses them: the
/api/v1/application/* endpoints use { error: { code, message } }, while the
three /sso/* ones use OAuth2's { error, error_description } (RFC 6749
§5.2). Both land here with code populated, so integrating code branches on
one field either way.
Extends
Error
Extended by
Constructors
Constructor
new ConnectError(init: {
message: string;
status: number;
code: string;
endpoint: string;
retryAfterMs?: number | null;
body?: unknown;
cause?: unknown;
}): ConnectError;Parameters
| Parameter | Type |
|---|---|
init | { message: string; status: number; code: string; endpoint: string; retryAfterMs?: number | null; body?: unknown; cause?: unknown; } |
init.message | string |
init.status | number |
init.code | string |
init.endpoint | string |
init.retryAfterMs? | number | null |
init.body? | unknown |
init.cause? | unknown |
Returns
ConnectError
Overrides
Error.constructorProperties
name
readonly name: "ConnectError" = "ConnectError";Overrides
Error.namestatus
readonly status: number;HTTP status. 0 when the request never got a response (network failure, timeout).
code
readonly code: string;rate_limited, forbidden, invalid_token, ... - branch on this, never on message.
retryAfterMs
readonly retryAfterMs: number | null;From Retry-After on a 429, in milliseconds. null otherwise.
endpoint
readonly endpoint: string;The endpoint that failed, e.g. POST /sso/token - present on every error.
body
readonly body: unknown;Parsed response body, when there was one.
stackTraceLimit
static stackTraceLimit: number;The Error.stackTraceLimit property specifies the number of stack frames
collected by a stack trace (whether generated by new Error().stack or
Error.captureStackTrace(obj)).
The default value is 10 but may be set to any valid JavaScript number. Changes
will affect any stack trace captured after the value has been changed.
If set to a non-number value, or set to a negative number, stack traces will not capture any frames.
Inherited from
Error.stackTraceLimitcause?
optional cause?: unknown;The cause of the error.
Inherited from
Error.causemessage
message: string;Inherited from
Error.messagestack?
optional stack?: string;Inherited from
Error.stackAccessors
isRetryable
Get Signature
get isRetryable(): boolean;Whether trying the exact same request again could plausibly succeed -
throttling, a transient server fault, or no response at all. The SDK
already retries these internally (see ConnectClientOptions.maxRetries);
this is for callers deciding whether to queue their own retry after the
SDK has given up.
A 503 is retryable only when it carries Retry-After. Connect sends
that header on every 503 it means to be retried - a dependency blip, a
contended transaction - and leaves it off when the dependency is
misconfigured (a revoked provider key, say), where every retry gets the
identical answer. Retrying those anyway cost a page three round trips and
two sleeps per read to learn nothing.
Returns
boolean
isSessionExpired
Get Signature
get isSessionExpired(): boolean;Whether the user's Connect sign-in is over and they need to go back
through identity.authorizeUrl(). Collapses every session-side failure,
matching how the API reports them - unknown, expired, revoked, or issued
to a different application all arrive as one invalid_token.
Returns
boolean
isKeyRejected
Get Signature
get isKeyRejected(): boolean;Whether your own API key is dead - revoked or past its expiry - rather than the request being wrong.
Connect answers 403 for every credential failure and keeps the code
uniform (forbidden) whenever telling them apart would help someone
guessing keys. These two are the exception: reaching them required
presenting a key whose hash matched, so you already hold it, and what you
need to know is that no retry and no scope change will fix this - the key
has to be reissued from the application's Connect client section.
Worth alerting on. It is the one failure that will not resolve itself and takes down every call your integration makes.
Returns
boolean
isAiCreditsExhausted
Get Signature
get isAiCreditsExhausted(): boolean;Whether the workspace's AI credits are spent - this month's included
credits used up and no purchased credits left. Nothing broke and
retrying changes nothing until the refill or a pack purchase; see
credits for the meter to show.
Returns
boolean
credits
Get Signature
get credits(): AiCreditUsage | null;The AI credit meter Connect sent with an ai_credits_exhausted refusal,
so the "out of credits" state can say when it refills and offer
topUpPath without a second read. null for every other error, and for
a refusal from a Connect that did not attach one.
Returns
AiCreditUsage | null
Methods
captureStackTrace()
Call Signature
static captureStackTrace(targetObject: object, constructorOpt?: Function): void;Creates a .stack property on targetObject, which when accessed returns
a string representing the location in the code at which
Error.captureStackTrace() was called.
const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`The first line of the trace will be prefixed with
${myObject.name}: ${myObject.message}.
The optional constructorOpt argument accepts a function. If given, all frames
above constructorOpt, including constructorOpt, will be omitted from the
generated stack trace.
The constructorOpt argument is useful for hiding implementation
details of error generation from the user. For instance:
function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();Parameters
| Parameter | Type |
|---|---|
targetObject | object |
constructorOpt? | Function |
Returns
void
Inherited from
Error.captureStackTraceCall Signature
static captureStackTrace(targetObject: object, constructorOpt?: Function): void;Create .stack property on a target object
Parameters
| Parameter | Type |
|---|---|
targetObject | object |
constructorOpt? | Function |
Returns
void
Inherited from
Error.captureStackTraceprepareStackTrace()
static prepareStackTrace(err: Error, stackTraces: CallSite[]): any;Parameters
| Parameter | Type |
|---|---|
err | Error |
stackTraces | CallSite[] |
Returns
any
See
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
Inherited from
Error.prepareStackTraceisError()
static isError(value: unknown): value is Error;Check if a value is an instance of Error
Parameters
| Parameter | Type | Description |
|---|---|---|
value | unknown | The value to check |
Returns
value is Error
True if the value is an instance of Error, false otherwise
Inherited from
Error.isErrorInterfaces
Interface: AddonUsage
One consumable add-on's remaining balance on a workspace's subscription.
Properties
| Property | Type | Description |
|---|---|---|
addonId | string | - |
name | string | - |
unit | string | What one unit is called - "messages", "credits", "minutes". |
includedPerCycle | number | Units granted at the start of each billing cycle. |
balance | number | Units left to spend. Can exceed includedPerCycle when carryForward, and goes negative only when overagePolicy is "bill" - that is accrued overage, floored at -maxOverageUnits. |
carryForward | boolean | - |
overagePolicy | "reject" | "bill" | - |
overagePricePerUnit | number | null | GST-exclusive paise per unit past the balance. null under "reject". |
maxOverageUnits | number | null | - |
Interface: AddonUsageResponse
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
applicationId | string | - |
addons | AddonUsage[] | Empty when the workspace has no subscription, the plan has no consumable add-ons, or none are selected. All three mean "nothing to meter" and none is an error. Note there is no "used this cycle" figure: with carryForward a balance survives the cycle boundary, so includedPerCycle - balance can go negative and would render as a meter running backwards. Show balance against includedPerCycle. |
resetsAt | string | null | ISO 8601. End of the current billing cycle, when balances are re-granted. |
Interface: AiActionsInput
Properties
| Property | Type |
|---|---|
title? | string |
actions | { label: string; href: string; detail?: string; }[] |
Interface: AiChartInput
Properties
| Property | Type | Description |
|---|---|---|
title? | string | - |
kind | "bar" | "line" | "area" | "pie" | - |
xLabel? | string | - |
yLabel? | string | - |
valueFormat | AiValueFormat | - |
series | { name: string; points: { x: string; y: number | null; }[]; }[] | Every series shares the same x values, in the same order. |
caption? | string | - |
Interface: AiConversation
One row of Ask AI history.
A conversation exists only if the turns that made it carried a
conversationId and were sent on a user's behalf - see AiResource.chat.
It belongs to one person in one of your workspaces, and never leaves your
application: a workspace subscribing to two products keeps two lists.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
title | string | Derived from the first thing the user said - no model call, so it costs nothing. |
createdAt | string | ISO-8601. |
updatedAt | string | ISO-8601, and the sort key - a conversation returned to a week later is at the top, not where it was created. |
Interface: AiConversationDetail
Properties
| Property | Type |
|---|---|
conversation | AiConversation |
turns | AiConversationTurn[] |
Interface: AiConversationList
Properties
| Property | Type | Description |
|---|---|---|
conversations | AiConversation[] | - |
nextCursor | string | null | Pass back as cursor for the next page; null at the end. |
Interface: AiConversationTurn
One stored turn, in the AI SDK's own shape.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
role | "assistant" | "user" | - |
parts | unknown[] | The message's parts exactly as the model produced them - prose, reasoning and tool calls, in the order they arrived. Opaque on purpose. The shape is the AI SDK's and moves with it, so a type restating it here would start rejecting transcripts the moment the SDK adds a part kind. Flattening it to text is the other mistake: the order is the model's own argument, and a flattened turn cannot be resumed. |
sequence | number | Position in the conversation. Turns arrive in this order. |
createdAt | string | ISO-8601. |
Interface: AiCreditRefundInput
What ai.refundCredits takes - gives one earlier spend back, in full.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
spendIdempotencyKey | string | The idempotencyKey the spend was made with. |
idempotencyKey | string | This refund's own key. One refund per spend; a replay answers the original. |
Interface: AiCreditRefundResult
What ai.refundCredits answers.
Properties
| Property | Type | Description |
|---|---|---|
entryId | string | The ledger entry - refund:<idempotencyKey>. |
refunded | number | Credits given back - the whole spend. |
credits | AiCreditUsage | The pool after the refund. |
Interface: AiCreditSpendInput
What ai.spendCredits takes - an explicit debit from the workspace's AI credit pool.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | Whose pool this spends. Your application must be subscribed there (or included). |
credits | number | Whole credits, 1-1000. |
feature | string | A slug naming the feature, at most 40 characters - ask-step, web-search. Shown on the customer's history. |
description | string | What the customer reads on their history, at most 200 characters. |
reference? | string | null | Your own id for the thing this paid for - a turn, a run. |
userId? | string | null | The Connect user who triggered it, when there was one - attribution only. |
idempotencyKey | string | Required, at most 128 characters, and enforced by Connect's ledger: the same key with the same credits answers the original spend, the same key with a different amount is 409 key_reused. So a retry is always safe. |
Interface: AiCreditSpendResult
What ai.spendCredits answers.
Properties
| Property | Type | Description |
|---|---|---|
entryId | string | The ledger entry - spend:<idempotencyKey>. |
charged | number | Credits this spend took. |
credits | AiCreditUsage | The pool after the spend - render it, no second read needed. |
Interface: AiCreditUsage
The signed-in user's workspace's AI credits this month - what
<ConnectAskAi> shows in its header, from connect.ai.getCredits or the
bridge's GET /ai/credits.
Credits are Connect's only unit for AI: tokens are never on the wire. The pool is per workspace and shared by every Ask AI panel and every generation run for it, so this is the same number Connect's own panel shows.
Properties
| Property | Type | Description |
|---|---|---|
unit | "credits" | - |
used | number | - |
included | number | - |
remaining | number | Rounded up while turns are still served; 0 exactly when state is "exhausted". |
percent | number | Whole percent spent, 0-100. |
state | "ok" | "warning" | "exhausted" | "warning" from 80% spent; "exhausted" while /ai/chat refuses with ai_credits_exhausted. |
period | string | YYYY-MM, UTC. |
resetsAt | string | ISO-8601 instant the allowance refills. |
purchased | number | Credits bought in packs, still unspent - included in remaining, and spent only once this month's included is used up. 0 when the workspace holds none; a pack never resets with the month. |
purchasedExpiresAt | string | null | ISO-8601 expiry of the soonest-expiring pack still holding credits; null with none. |
topUpPath | string | Connect's page for buying more (/billing/ai-credits). Hand it to useConnect().openConnect() rather than linking it - that is what brings the customer back to your app when the purchase is done. |
canBuy | boolean | Whether credit packs are on sale to this workspace. Offer "Buy credits" only when it is. |
Interface: AiDetailsInput
Properties
| Property | Type |
|---|---|
title? | string |
fields | { label: string; value: AiValue; format: AiValueFormat; tone: AiTone; }[] |
Interface: AiGenerateInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | Whose AI budget this spends. Your application must be installed there. |
model? | string | One of the models Connect allows. Omitted means Connect's default. |
system? | string | null | - |
messages | AiGenerateMessage[] | - |
tools? | AiGenerateTool[] | - |
toolChoice? | | "none" | { tool: string; } | "auto" | "required" | - |
responseSchema? | Record<string, unknown> | null | A JSON Schema. When given, the parsed answer is output. |
maxOutputTokens? | number | Defaults to 2048; at most 8192. |
temperature? | number | null | - |
thinkingLevel? | "medium" | "low" | "high" | null | - |
metering? | AiMetering | How this call counts against AI credits - see AiMetering. Omitted means "tokens". |
Interface: AiGenerateResult
Properties
| Property | Type | Description |
|---|---|---|
model | string | - |
text | string | - |
output | unknown | The parsed answer when responseSchema was given and the model produced one; null otherwise. |
toolCalls | AiGenerateResultToolCall[] | Tools the model asked for. Run them and send the results back on your next call, with these calls - providerMetadata included - on the assistant message. |
finishReason | string | stop, length, tool-calls, content-filter, ... |
usage | { inputTokens: number; outputTokens: number; } | What this call cost, as counted against the workspace's monthly budget. |
usage.inputTokens | number | - |
usage.outputTokens | number | - |
Interface: AiGenerateResultToolCall
A tool call the model asked for. Pass it back as-is, providerMetadata included.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
input | unknown | - |
providerMetadata | Record<string, unknown> | null | Opaque provider metadata (Gemini 3's thought signature). null when the provider attached none. Carry it back verbatim. |
Interface: AiGenerateTool
A tool the model may ask you to run - see ai.generate. Never executed by Connect.
Properties
| Property | Type | Description |
|---|---|---|
name | string | Letters, digits, _ and -, up to 64. |
description | string | - |
inputSchema | Record<string, unknown> | A JSON Schema object for the tool's input. |
Interface: AiGenerateToolCall
A tool call you send back on an assistant message - see AiGenerateResultToolCall.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
input | unknown | - |
providerMetadata? | Record<string, unknown> | null | What providerMetadata was on the result that produced this call. Send it back unchanged - it is Gemini 3's thought signature, and a replayed call without it costs the model its own reasoning across the step, so a multi-step loop degrades one step at a time. Opaque; never read it. |
Interface: AiMetricsInput
Properties
| Property | Type |
|---|---|
title? | string |
metrics | { label: string; value: AiValue; format: AiValueFormat; delta?: number | null; deltaMeaning: "neutral" | "up-is-good" | "down-is-good"; hint?: string; }[] |
Interface: AiReadPageInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | Whose AI allowance this sits under. Your application must be installed there. |
url | string | An absolute http(s) URL on a public host. Private and loopback hosts are refused (validation_error). |
metering? | AiMetering | How this call counts against AI credits - see AiMetering. Omitted means "tokens". |
Interface: AiReadPageResult
One public page as readable text - see ai.readPage.
Properties
| Property | Type | Description |
|---|---|---|
url | string | The URL actually read, after redirects (each hop host-checked). Cite this one. |
title | string | null | - |
text | string | Readable text, paragraphs kept, at most 20,000 characters. |
truncated | boolean | Whether text was cut at the limit. |
Interface: AiSourcesInput
Properties
| Property | Type |
|---|---|
sources | { title: string; uri?: string; snippet?: string; }[] |
Interface: AiStepsInput
Properties
| Property | Type |
|---|---|
title? | string |
steps | { title: string; detail?: string; }[] |
Interface: AiTableInput
Properties
| Property | Type | Description |
|---|---|---|
title? | string | - |
columns | { label: string; format: AiValueFormat; }[] | - |
rows | AiValue[][] | Positional: one array per row, values in the same order as columns. |
caption? | string | - |
Interface: AiTimelineInput
Properties
| Property | Type | Description |
|---|---|---|
title? | string | - |
events | { at: string; title: string; detail?: string; tone: AiTone; }[] | Oldest first. |
Interface: AiWebSearchInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | Whose AI budget this spends. Your application must be installed there. |
query | string | What to search for, 1-500 characters. |
metering? | AiMetering | How this call counts against AI credits - see AiMetering. Omitted means "tokens". |
Interface: AiWebSearchResult
A grounded Google search - see ai.webSearch.
searchSuggestionsHtml must be displayed. Grounding with Google Search
requires Google's rendered Search Suggestions to be shown next to a grounded
result wherever that result is shown to a person. Render it as given,
beside answer.
Properties
| Property | Type | Description |
|---|---|---|
answer | string | The grounded summary. May be "" when the search found nothing to say. |
sources | { url: string; title: string; }[] | The pages the answer leaned on, deduplicated. |
searchSuggestionsHtml | string | null | Google requires this rendered next to the result wherever it is shown. null when the model did not search. |
usage | { inputTokens: number; outputTokens: number; } | What this call cost, as counted against the workspace's monthly budget. |
usage.inputTokens | number | - |
usage.outputTokens | number | - |
Interface: AnalyticsPeriod
A period of whole local days, both ends inclusive, and the period it is compared against.
Properties
| Property | Type | Description |
|---|---|---|
preset | | "today" | "yesterday" | "last_7_days" | "last_14_days" | "last_30_days" | "last_90_days" | "this_week" | "last_week" | "this_month" | "last_month" | "this_quarter" | "this_year" | "custom" | - |
from | string | - |
to | string | - |
days | number | Inclusive day count. |
previousFrom | string | The comparison period. A rolling window compares with the same number of days immediately before it; a to-date period (this_month) with the same stretch of the previous one (1st-to-the-7th against 1st-to-the-7th); a whole calendar period (last_month) with the whole one before it. |
previousTo | string | - |
timeZone | string | The zone the days are in. |
Interface: AnalyticsPoint
Properties
| Property | Type | Description |
|---|---|---|
day | string | YYYY-MM-DD, UTC. |
value | number | null | null is not zero. It means the metric was not measurable that day - usage.active_users is a distinct count that only exists after Connect's nightly rollup, so today's is always null, as is any future day. Render it as a gap or a dash, never as a zero. |
Interface: AnalyticsQuery
Properties
| Property | Type | Description |
|---|---|---|
metrics | ConnectMetric[] | - |
from | string | Inclusive YYYY-MM-DD, UTC. The range must be shorter than 90 days. |
to | string | - |
Interface: AnalyticsResponse
Properties
| Property | Type |
|---|---|
from | string |
to | string |
series | AnalyticsSeries[] |
Interface: AnalyticsSeries
Properties
| Property | Type | Description |
|---|---|---|
metric | ConnectMetric | - |
points | AnalyticsPoint[] | - |
total | number | null | Sum across measurable days only, or null if no day in the range was measurable. |
partial | boolean | True when at least one day was not measurable - caveat the total when this is set. |
Interface: ApplicationAnalyticsDimension
One dimension your application declared - what groupBy and filter may name.
Properties
| Property | Type | Description |
|---|---|---|
key | string | - |
label | string | - |
maxCardinality | number | Distinct values kept per day; the rest fold into "_other". |
Interface: ApplicationAnalyticsMetric
One metric your application declared - see analytics.schema().
Properties
| Property | Type | Description |
|---|---|---|
key | string | Dot-namespaced, stable, e.g. deals.won_value. |
label | string | - |
event | string | The event name this metric reads, e.g. deal.won. |
agg | AnalyticsAggregation | - |
unit? | string | null | Unit of value for sum/gauge (paise, bytes, ms, ...). |
description? | string | null | - |
Interface: ApplicationAnalyticsSchema
GET /analytics/schema - what your application declared, edited in Console.
Properties
| Property | Type |
|---|---|
metrics | ApplicationAnalyticsMetric[] |
dimensions | ApplicationAnalyticsDimension[] |
Interface: ApplicationConnectionSummary
One connection your application is a party to, in one workspace -
connect.applications.connections(). consumer/provider are application
keys; scopes is the effective set a token can carry today (empty once
revoked).
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
consumer | string | - |
provider | string | - |
status | "active" | "revoked" | - |
scopes | string[] | - |
origin | "consent" | "first_party_auto" | - |
grantedAt | string | ISO-8601. |
pendingScopes | string[] | Scopes a reconnect would add: what the consumer declares and the provider exposes now, minus what the workspace granted. Non-empty means the grant predates a widened declaration - it still works at the old scope, and the pair "Needs reconnecting". Always empty for a revoked connection and when the grant is up to date. |
Interface: ApplicationEventEnvelope
An event another application published, as your webhook endpoint receives
it - the data of an application.event delivery.
Properties
| Property | Type | Description |
|---|---|---|
id | string | Connect's id. Deduplicate on it - delivery is at-least-once. |
type | string | - |
workspaceId | string | - |
source | string | The publisher's application key. |
occurredAt | string | ISO-8601. |
data | Record<string, unknown> | - |
Interface: ApplicationMetricPoint
Properties
| Property | Type | Description |
|---|---|---|
bucket | string | Bucket start (YYYY-MM-DD: the day, the Monday, or the 1st), or "total". |
value | number | null | null is not zero - the bucket is not measured yet: a past day the nightly rollup has not reached, a distinct metric today, or a future day. Draw it as a gap. |
Interface: ApplicationMetricSeries
Properties
| Property | Type | Description |
|---|---|---|
metric | string | - |
label | string | - |
agg | AnalyticsAggregation | - |
unit | string | null | - |
dimValue | string | null | The groupBy value this series is for; null when ungrouped. |
points | ApplicationMetricPoint[] | - |
total | number | null | Combined by the metric's own rule - sum, latest level, or merged distinct users. null when nothing was measurable. |
partial | boolean | True when any day in range was not measurable - caveat the total. |
Interface: ApplicationMetricsQuery
analytics.metrics(query) - a read of declared metrics.
Properties
| Property | Type | Description |
|---|---|---|
metrics | string[] | Declared metric keys. An undeclared one is a 422 naming it. |
from | string | Inclusive local days, YYYY-MM-DD, in the workspace's time zone. At most 400 days. |
to | string | - |
granularity? | AnalyticsGranularity | Defaults to day. |
groupBy? | string | null | A declared dimension: one series per value, highest volume first. |
filter? | | { dimKey: string; dimValue: string; } | null | Restrict to one value of one declared dimension. |
workspaceId? | string | null | The workspace to read, verified against your subscriptions. Omit for the application-wide total across every workspace - a server-side number; never hand it to a browser. |
Interface: ApplicationMetricsResponse
Properties
| Property | Type | Description |
|---|---|---|
from | string | - |
to | string | - |
granularity | AnalyticsGranularity | - |
timeZone | string | The zone the local days are in. |
series | ApplicationMetricSeries[] | - |
Interface: ApplicationPermission
Properties
| Property | Type |
|---|---|
id | string |
label | string |
description | string |
Interface: ApplicationRoleDefinition
Properties
| Property | Type |
|---|---|
key | string |
name | string |
description | string |
permissions | string[] |
Interface: ApplicationSubscriber
One row of subscribers.list() - a workspace subscribed to your application.
Properties
| Property | Type | Description |
|---|---|---|
subscriptionId | string | - |
workspaceId | string | - |
workspaceName | string | null | null when the workspace has been purged and only its tombstone remains. |
workspaceStatus | string | null | "active", "suspended", "pending_deletion", "deleted" - or null alongside a null workspaceName. |
planId | string | - |
planName | string | null | null if the plan was hard-deleted rather than archived. |
status | "created" | "authenticated" | "active" | "paused" | "cancelled" | - |
seatCount | number | The last-billed seat snapshot, not the live count SubscriptionDetail carries: a page of subscribers cannot afford one member scan per row. It is 0 unless the plan is "per-seat", which is the only model that bills by seat. Read the single workspace's subscription for "who is assigned right now". |
subscribedAt | string | ISO 8601. |
nextBillingDate | string | null | ISO 8601, or null for a cancelled/paused subscription. |
Interface: AppliedDiscount
Properties
| Property | Type | Description |
|---|---|---|
source | "coupon" | "admin" | - |
code | string | null | Display code - null for an admin-applied discount with no coupon behind it. |
discountType | "fixed" | "percentage" | - |
value | number | 1-90 for "percentage"; GST-exclusive paise for "fixed". |
duration | "once" | "repeating" | "forever" | - |
cyclesRemaining | number | null | Decremented each captured cycle. null only for "forever". |
Interface: ArchivedRecording
A recording moved into the workspace's Drive, as a file your application uploaded.
Properties
| Property | Type |
|---|---|
fileId | string |
sizeBytes | number |
contentType | string |
Interface: ArchiveRecordingInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
callId | string | Your own call id - kept with the archive and in the file name. |
recordingUrl | string | The carrier's URL for the recording, from the recording event. |
Interface: AssessmentDraft
Properties
| Property | Type | Description |
|---|---|---|
assessmentKey | string | - |
title | string | - |
sections | AssessmentSection[] | The definition's sections, in the order they should be asked. |
answers | AssessmentAnswers | Suggested answers - a strawman to edit, never a finished answer. |
Interface: AssessmentFinding
Properties
| Property | Type | Description |
|---|---|---|
severity | AssessmentFindingSeverity | - |
sections | string[] | Section keys this finding concerns - usually two, since the useful findings are tensions between sections. |
title | string | - |
detail | string | - |
fix | string | The specific change to make. |
Interface: AssessmentReview
Properties
| Property | Type | Description |
|---|---|---|
assessmentKey | string | - |
findings | AssessmentFinding[] | - |
riskiestAssumption | { section: string; assumption: string; experiment: string; } | The one assumption everything rests on, and a cheap way to test it. Lead with this. |
riskiestAssumption.section | string | - |
riskiestAssumption.assumption | string | - |
riskiestAssumption.experiment | string | - |
score | number | 0-100, advisory and coarse - it shows movement between revisions. |
Interface: AssessmentSection
One question in an assessment, as the platform defines it. Echoed back on a draft so the questions can be rendered without a second round trip.
Properties
| Property | Type | Description |
|---|---|---|
key | string | Stable identifier - the key answers are filed under. |
question | string | The question as a person reads it. |
hint? | string | What belongs here. Written to steer generation, but useful as helper text. |
min? | number | Fewest answers a draft offers. |
max? | number | Most answers a draft offers, and the cap worth enforcing on edits. |
Interface: AvailabilitySchedule
A working-hours template an appointment type points at.
Properties
| Property | Type |
|---|---|
id | string |
name | string |
timezone | string |
weeklyHours | WeeklyHours |
Interface: BookableEventType
One bookable thing a workspace offers - "15-minute intro call", "site survey".
locationKind and never the location itself: the address or the meeting
link belongs to whoever booked, and this is a catalogue.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
slug | string | Stable and readable, unique within the workspace - branch on this, not id. |
name | string | - |
description | string | null | - |
durationMinutes | number | - |
locationKind | "phone" | "address" | "link" | - |
Interface: Booking
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
eventTypeId | string | - |
hostMemberIds | string[] | The workspace members committed to this appointment. These are ConnectWorkspaceMember.memberId values - what workspaces.members() returns as memberId and what listBookings' hostMemberId filter takes, so a host column is a lookup against a roster you already have rather than another round trip. Not userId. An earlier version of this comment said these were the ids workspaces.listMembers returns, which was wrong twice over: that method is workspaces.members(), and until memberId was added to its rows it returned no id these could be matched against at all. Resolved when the slot was held and frozen there: a round-robin event type picks one host, a collective one commits the whole group, and re-pointing the event type afterwards does not move an appointment already made. |
status | BookingStatus | - |
startAt | string | ISO-8601 instants. |
endAt | string | - |
timezone | string | The zone the two parties agreed the time in, frozen at booking. |
attendee | { name: string; phone: string; email: string | null; } | - |
attendee.name | string | - |
attendee.phone | string | - |
attendee.email | string | null | - |
notes | string | null | - |
source | BookingSource | - |
sourceCallId | string | null | The call or conversation this was booked during - the sourceCallId its maker passed, verbatim - or null. Your own call id, when you booked it. |
holdExpiresAt | string | null | ISO-8601. When an unconfirmed hold lapses and frees the slot - set only while status is hold. See scheduling.holdSlot. |
cancelledReason | string | null | - |
rescheduledToId | string | null | Set when status is rescheduled - the booking that replaced this one. Rescheduling retires a booking rather than moving its startAt, so an id you cached still resolves and still tells you where the appointment went. |
createdAt | string | - |
Interface: BookingAttendee
Properties
| Property | Type |
|---|---|
name | string |
phone | string |
email? | string | null |
Interface: BookingCancelledEvent
An appointment was cancelled. The slot is free and any prep against it is void.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
bookingId | string | - |
eventTypeId | string | - |
eventTypeName | string | - |
startAt | string | ISO-8601. The time it was going to happen. |
attendeeName | string | - |
contactId | string | null | - |
occurredAt | string | ISO-8601. |
Interface: BookingConfirmedEvent
An appointment was booked and is holding its slot.
Raised however it was booked - the customer's portal, the public booking
page, a voice agent mid-call, or your own API call - so you get one shape
for all four and never have to reconcile "did we create this, or did they?".
Dedupe on bookingId, not on the envelope alone.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
bookingId | string | - |
eventTypeId | string | - |
eventTypeName | string | - |
startAt | string | ISO-8601, UTC. Render it in timezone, which is the workspace's, not the viewer's. |
endAt | string | - |
timezone | string | IANA zone the appointment was booked in. |
attendeeName | string | - |
attendeeEmail | string | null | - |
attendeePhone | string | null | E.164. |
contactId | string | null | - |
hostMemberIds | string[] | Connect user ids of the hosts. |
occurredAt | string | ISO-8601. |
Interface: BookingRescheduledEvent
An appointment moved.
Connect retires the old booking and writes a new one rather than mutating
it, so this carries both ids: bookingId is the row that now exists and
previousBookingId is the one you are already holding. Reconcile on the
previous id or you will end up with two appointments.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
bookingId | string | - |
eventTypeId | string | - |
eventTypeName | string | - |
startAt | string | ISO-8601, the new time. |
endAt | string | - |
previousBookingId | string | - |
previousStartAt | string | ISO-8601, the time it was before. |
timezone | string | - |
attendeeName | string | - |
contactId | string | null | - |
occurredAt | string | ISO-8601. |
Interface: BookingSlot
Properties
| Property | Type | Description |
|---|---|---|
startAt | string | ISO-8601 instant. Pass this back to createBooking unchanged. |
endAt | string | - |
Interface: BookingSlots
Properties
| Property | Type | Description |
|---|---|---|
timezone | string | The business's IANA timezone, not yours and not your user's - what to render a slot's wall time in. A slot shown in the viewer's zone is an hour the business may not be open. |
durationMinutes | number | - |
slots | BookingSlot[] | - |
Interface: BrandCitation
Properties
| Property | Type | Description |
|---|---|---|
source | BrandCitationSource | - |
url | string | - |
retrievedAt | string | ISO-8601. |
Interface: BrandKit
Properties
| Property | Type | Description |
|---|---|---|
logos | BrandLogo[] | - |
palette | PaletteEntry[] | - |
typography | TypefaceEntry[] | - |
voice | { tagline: string | null; tone: string[]; } | - |
voice.tagline | string | null | - |
voice.tone | string[] | A few adjectives for the register the copy is written in. |
Interface: BrandLogo
Properties
| Property | Type | Description |
|---|---|---|
url | string | null | A short-lived signed URL, minted fresh on every read. Do not store it and do not bake it into a cached page - it will expire and become a broken image with a plausible-looking address. Render it directly, or re-read the profile, which on a cache hit is one document read. null when Connect holds no cached copy, in which case sourceUrl is all there is. |
sourceUrl | string | Where it lives on the company's own site. Stable, and the citation. |
kind | LogoKind | - |
format | string | Lowercase extension without the dot: svg, png, webp. |
width | number | null | - |
height | number | null | - |
Interface: BrandProfile
What a company looks like, measured off its own website.
The important property: the numbers and the colours here were parsed, not
generated. Every hex came out of a stylesheet and every scale step is a
count of real declarations. A model wrote only the prose (about,
industry, businessModel, offerings, voice) and assigned role to
values it was shown. Ask any model directly for a brand colour and it
answers #3B82F6; that is the failure this shape exists to avoid.
Properties
| Property | Type | Description |
|---|---|---|
domain | string | Normalised - lowercase, no scheme, no www.. |
knownAs | string | null | The trading name, as the company writes it. |
officialName | string | null | - |
officialNameSource | OfficialNameSource | null | - |
about | string | null | - |
industry | string | null | - |
businessModel | BusinessModel | null | - |
offerings | Offering[] | - |
brandKit | BrandKit | - |
designSystem | DesignSystem | - |
confidence | "medium" | "low" | "high" | Derived from how many aspects the sources actually filled - not a model's self-report. |
citations | BrandCitation[] | - |
retrievedAt | string | ISO-8601. Age the profile against this. |
Interface: BrowserCallingToken
A carrier access token a member's browser signs in to the phone network with. Valid for an hour.
Properties
| Property | Type | Description |
|---|---|---|
token | string | - |
expiresAt | string | ISO-8601. |
Interface: BrowserTokenInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
subject | string | 1-48 letters and digits. Connect prefixes it with a 19-character tag naming your application and the workspace. |
ttlSeconds | number | 60-3600. |
Interface: BrowserTokenResult
Properties
| Property | Type | Description |
|---|---|---|
token | string | - |
expiresAt | string | ISO-8601. |
Interface: BundleTax
Properties
| Property | Type | Description |
|---|---|---|
taxRatePercent | number | null | null when a component the rule depends on states no rate - see missingTaxRate. |
hsnSac | string | null | - |
basis | | { productId: string; name: string; } | null | The component the rate came from: the principal, or the highest-rated. |
missingTaxRate | { productId: string; name: string; }[] | The components whose missing rate left the bundle without one. |
Interface: BundleTaxSource
What deriveBundleTax needs about one component, in bundle order.
Properties
| Property | Type |
|---|---|
productId | string |
name | string |
taxRatePercent | number | null |
hsnSac | string | null |
Interface: CallActionItem
Something somebody committed to on a recorded call. owner is who said
they would do it - rep for your user's side, customer for the person
called. Both are worth a follow-up: one to do, one to chase.
Properties
| Property | Type | Description |
|---|---|---|
title | string | - |
dueDate | string | null | YYYY-MM-DD when a day was named or clearly implied, else null. |
owner | "rep" | "customer" | - |
Interface: CallAnalysis
Connect's reading of a call recording. rep is the business side.
Properties
| Property | Type | Description |
|---|---|---|
turns | { speaker: "rep" | "customer"; text: string; }[] | - |
summary | string | - |
intent | string | - |
sentiment | "positive" | "neutral" | "negative" | - |
actionItems | { title: string; dueDate: string | null; owner: "rep" | "customer"; }[] | - |
language | string | null | BCP-47, e.g. hi-IN. |
Interface: CallAnalysisInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
fileId | string | A Drive file your application uploaded in that workspace - e.g. ArchivedRecording.fileId. |
direction | "inbound" | "outbound" | - |
repName | string | null | The business-side speaker's name, when known. |
callDate | string | YYYY-MM-DD - what relative due dates are worked out from. |
Interface: CallAnalyzedEvent
A recorded call, transcribed and read. Sent minutes after call.completed,
and only for calls placed while the workspace records them - file the call
on call.completed and enrich that row here. The transcript itself is the
call's turns on calls.get.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
callId | string | - |
channelId | string | null | - |
contactPhone | string | null | E.164 - the person called on an outbound call, the caller on an inbound one. |
direction | ConnectCallDirection | - |
handler | CallHandler | - |
placedByUserId | string | null | - |
placedByName | string | null | - |
summary | string | null | Two or three sentences, in English whatever language was spoken. |
intent | string | null | Why they were on the call, in a few lowercase words - "pricing enquiry". |
sentiment | CallSentiment | null | - |
language | string | null | The main language spoken, BCP-47 - hi-IN, en-IN. |
actionItems | CallActionItem[] | - |
recordingFileId | string | null | The audio in the workspace's Drive - read it with a drive:read key. |
occurredAt | string | ISO-8601 - when the call started. |
Interface: CallCompletedEvent
A call ended.
The event an external system of record is built on: it is what turns a conversation that happened in Connect's telephony into a row in your own timeline with nobody retyping it. Fires for every completed call, whether an agent answered it or a person did.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
callId | string | - |
channelId | string | null | - |
contactPhone | string | null | E.164, and the only identity a call event carries. There is deliberately no contactId: Connect resolves a caller to a contact by number, and at the moment a call ends it may not have done so at all - the field would be null on most deliveries, which is a field that silently does nothing. Match on this instead. It is the same canonical form contacts.lookupByPhone takes, so resolving it is one call if you want the contact. |
direction | ConnectCallDirection | - |
outcome | string | How it ended, as Connect's telephony reported it - "completed", "transferred", … |
durationSeconds | number | - |
extraction | Record<string, unknown> | What the agent's skills captured during the conversation, keyed by the capture fields the agent declares. Shapeless on purpose - the keys and the value types are whatever that agent's extractionSchema declares, which Connect does not constrain. {} on most calls, since an unanswered or human-answered call extracts nothing, so treat every key as optional rather than checking the object for presence. |
handler? | CallHandler | member when a person placed this call from a browser - <ConnectCallButton> or Connect's own portal - rather than an agent handling it. Absent on events sent before member calling existed; treat absent as agent. |
placedByUserId? | string | null | The member who placed a member call - their Connect user id, the same id a workspace roster carries. The one to attribute the call to. null on an agent's call. |
placedByName? | string | null | That member's display name, else their email - so the call can be attributed without a roster read. |
occurredAt | string | ISO-8601. |
Interface: CallDetail
A call and its transcript. Long calls are capped at 2,000 turns.
Properties
| Property | Type |
|---|---|
call | ConnectCall |
turns | CallTranscriptTurn[] |
Interface: CallingAvailability
Properties
| Property | Type | Description |
|---|---|---|
status | CallingAvailabilityStatus | - |
number | string | null | The workspace's business number, E.164, when it holds one. |
available | number | Prepaid balance a new call may spend, in paise, pre-tax. |
availableMinutes | number | About how many minutes that buys. |
ratePaisePerMinute | number | - |
lowBalance | boolean | Under about 55 minutes - worth a warning. |
canTopUp | boolean | Whether this member may buy minutes. |
canManageNumber | boolean | Whether this member may add the number add-on or retry a pending number. |
offer | | { subscriptionId: string; applicationId: string; addonId: string; addonName: string; price: number; } | null | Where a business number can be bought, when the workspace has none. |
Interface: CallingReadiness
Where the workspace stands on the steps Indian telecom rules ask of a
business before it calls - connect.calls.getCallingReadiness(). Read
runs, not steps, to decide what to offer: Connect evaluates them with
the gates the dialer refuses on.
Properties
| Property | Type | Description |
|---|---|---|
intent | "service_only" | "service_and_sales" | null | - |
progress | { done: number; required: number; } | - |
progress.done | number | - |
progress.required | number | - |
steps | CallingReadinessStep[] | - |
runs | CallingReadinessRun[] | - |
setupUrl | string | Connect's journey screen, carrying your client_id when SSO is on. |
canManage | boolean | Whether this member may take the steps - their channel:manage. |
Interface: CallingReadinessRun
One kind of calling, and whether the workspace may run it today.
Properties
| Property | Type | Description |
|---|---|---|
purpose | "service" | "promotional" | service covers existing customers; promotional is any sales call. |
mode | "preview" | "automatic" | preview: a rep presses dial. automatic: software paces the dialing. |
allowed | boolean | - |
reason | string | null | The first thing in the way, worded for the member. null when allowed. |
Interface: CallingReadinessStep
A step of Connect's calling compliance journey - connect.calls.getCallingReadiness().
Properties
| Property | Type | Description |
|---|---|---|
key | | "number" | "business" | "intent" | "header" | "autodialer" | "consent" | "rules" | - |
label | string | - |
status | "done" | "not_started" | "submitted" | "returned" | - |
required | boolean | Whether the business needs it for the calls it said it makes. |
note | string | null | A returned step's reason, or what the numbers say. |
Interface: CallMissedEvent
A call reached a channel and nothing answered it.
Also raised for a call a member placed that the person rang did not pick
up (handler: "member", direction: "outbound") - somebody to try again.
Its own event rather than an outcome on the one above, because the action
differs in kind: a completed call is something to file, a missed one is
somebody to ring back. Treating this as a lead is the right thing to do
with it.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
callId | string | - |
channelId | string | null | - |
contactPhone | string | null | E.164, and the only identity this carries - see CallCompletedEvent.contactPhone. |
direction | ConnectCallDirection | - |
handler? | CallHandler | member when a person placed this call from a browser - <ConnectCallButton> or Connect's own portal - rather than an agent handling it. Absent on events sent before member calling existed; treat absent as agent. |
placedByUserId? | string | null | The member who placed a member call - their Connect user id, the same id a workspace roster carries. The one to attribute the call to. null on an agent's call. |
placedByName? | string | null | That member's display name, else their email - so the call can be attributed without a roster read. |
occurredAt | string | ISO-8601. |
Interface: CallSettings
The workspace's calling settings, as the signed-in member sees them -
connect.calls.getCallSettings().
Properties
| Property | Type | Description |
|---|---|---|
recordingEnabled | boolean | Whether member calls are recorded, transcribed and read (call.analyzed). |
recordingRatePaisePerMinute | number | Paise, pre-tax, recording adds to every billed minute - show it beside the switch. |
canManage | boolean | Whether this member may change it - their channel:manage. Draw the switch disabled when not. |
Interface: CallsPage
One page of calls, newest first. Page until hasMore is false - do not stop on an empty items.
Properties
| Property | Type |
|---|---|
items | ConnectCall[] |
nextCursor | string | null |
hasMore | boolean |
Interface: CallTokenUsage
What the model leg of a call cost.
Properties
| Property | Type |
|---|---|
promptTokens | number |
responseTokens | number |
totalTokens | number |
Interface: CallToolInvocation
One tool the agent called mid-conversation. Arguments and results are JSON strings, not parsed objects.
Properties
| Property | Type | Description |
|---|---|---|
name | string | - |
argumentsJson | string | - |
resultJson | string | null | - |
durationMs | number | null | - |
startedAt | string | null | null for calls closed out before the runtime reported it - which is not zero. |
error | string | null | - |
Interface: CallTranscriptTurn
One transcript turn. Order by sequence, never by startedAt - two turns
can share a millisecond.
Properties
| Property | Type | Description |
|---|---|---|
sequence | number | - |
role | CallTurnRole | - |
text | string | - |
startedAt | string | null | - |
interrupted | boolean | True only ever for an agent turn - the caller talked over it. |
Interface: ChannelRouting
Where inbound conversations on a channel go.
destination is yours: an arbitrary JSON object Connect stores verbatim
and never reads into. Which agent answers, business hours, overflow - all of
it is your application's model, not Connect's.
Properties
| Property | Type |
|---|---|
destination | Record<string, unknown> |
updatedAt | string |
Interface: ChannelRoutingRecord
What both routing writes echo back - the stored record, not the whole
channel. routing is null after clearRouting(), which is the same shape
a channel that was never routed has: to the answer path "cleared" and "never
set" are the same fact.
Properties
| Property | Type |
|---|---|
id | string |
routing | ChannelRouting | null |
Interface: ChannelsPage
One page of channels. Page until hasMore is false.
Properties
| Property | Type |
|---|---|
items | ConnectChannel[] |
nextCursor | string | null |
hasMore | boolean |
Interface: ChatMessage
Indexable
[field: string]: unknownProperties
| Property | Type |
|---|---|
role | "assistant" | "user" | "system" |
Interface: ClaimedChannel
What a claim answers with - narrower than ConnectChannel, because a
just-claimed number has no label and no routing yet. Read it back with
channels.get(id) once you have written routing.
Properties
| Property | Type |
|---|---|
id | string |
kind | ChannelKind |
address | string |
workspaceId | string |
status | ChannelStatus |
Interface: CompanyProfile
Who operates the platform your application runs on, and how a user complains to them.
You are a Data Processor on this notice, not a second Data Fiduciary - so
this is the block your privacy page and your consent capture print, rather
than one of your own kept in environment variables. That is the whole point
of reading it: a statutory contact copied into .env goes stale silently,
and every application holding a copy publishes a channel that reaches
nobody.
Properties
| Property | Type | Description |
|---|---|---|
legalName | string | - |
brandName | string | Trading name, for prose the full registered name reads oddly in. Editable by the platform operator since SDK 0.23. It was a hardcoded constant on Connect's side before that - the one company fact no operator could correct without a deploy. |
registeredAddress | string | - |
contactEmail | string | - |
website | string | null | - |
gstNumber | string | null | GSTIN, null if the operating entity is not GST-registered. |
legalJurisdiction | string | null | The seat a governing-law clause names - "Chennai, Tamil Nadu". Not the registered address, and the distinction is the reason it is its own field: an entity registered in one state routinely names another as the venue for disputes, and the two copies of this that applications used to keep disagreed about which. null when the operator has not published one, in which case a terms page should omit the venue rather than reach for registeredAddress instead. |
grievance | GrievanceChannel | - |
Interface: CompanyProfileResponse
Properties
| Property | Type |
|---|---|
companyProfile | CompanyProfile |
Interface: ConnectAccessToken
A token as connect.applications.token() hands it back.
Properties
| Property | Type | Description |
|---|---|---|
accessToken | string | - |
tokenType | "Bearer" | - |
expiresIn | number | Seconds, as the endpoint answered at issuance. |
expiresAt | number | Epoch milliseconds, computed locally from expiresIn at receipt. |
scopes | string[] | The scopes the token actually carries - may be narrower than asked for only if Connect says so. |
issuedTokenType? | "urn:ietf:params:oauth:token-type:access_token" | Present on a delegated (token-exchange) token. |
Interface: ConnectAiStreamErrorEvent
The error event as it appears on the wire - thrown by ai.stream, never yielded.
Properties
| Property | Type |
|---|---|
type | "error" |
code | string |
message | string |
Interface: ConnectCall
One call that arrived on a channel assigned to your application.
durationSeconds and billDurationSeconds are the two durations Connect
stands behind - the carrier's own measure of the audio is deliberately not
published, since a third near-identical number only invites a billing
argument settled off the wrong one.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
channelId | string | - |
workspaceId | string | - |
direction | CallDirection | - |
fromNumber | string | - |
toNumber | string | - |
status | CallStatus | - |
rejectReason | | string & { } | CallRejectReason | null | - |
disconnectReason | | string & { } | CallDisconnectReason | null | - |
startedAt | string | - |
answeredAt | string | null | - |
endedAt | string | null | - |
durationSeconds | number | null | - |
billDurationSeconds | number | null | - |
recordingStatus | CallRecordingStatus | - |
recordingFileId | string | null | A Drive file id - read it with a drive:read key. null until the carrier posts the audio. |
transcriptFileId | string | null | - |
turnCount | number | null | - |
tokenUsage | CallTokenUsage | null | - |
toolInvocations | CallToolInvocation[] | - |
extraction | Record<string, unknown> | null | Your agent's own post-call structured output, keyed by its extraction schema. Connect stores it without interpreting it. |
agentId | string | null | - |
agentVersion | number | null | - |
handler | CallHandler | member for a call a person placed with <ConnectCallButton>. Such a call has no agent; it has a recording, a transcript and an analysis only when the workspace records calls. |
placedByUserId | string | null | The member who placed a member call - their Connect user id. |
talkSeconds | number | null | How long the person called was on the line, for a member call. Read this rather than durationSeconds, which there measures the member's own leg, ringing included. null until the carrier reports it, and for a call nobody answered. |
dialledAt | string | null | When Connect asked the carrier to ring the person called, on a member call. Tells apart the two calls unansweredReason: "cancelled" covers: set when the member hung up while it rang (the person sees a missed call - Connect sends call.missed), null when they gave up before anyone was rung (no call happened, and no event is sent). |
unansweredReason | | string & { } | CallUnansweredReason | null | Why nobody answered - set only when status is unanswered. |
analysis | ConnectCallAnalysis | null | The reading of a recorded member call - summary, intent, action items. null for a call that was not recorded; status: "pending" while it is being read. The transcript is CallDetail.turns, the member as agent and the person called as user. |
Interface: ConnectCallAnalysis
Properties
| Property | Type |
|---|---|
status | "pending" | "failed" | "done" |
summary | string | null |
intent | string | null |
sentiment | CallSentiment | null |
actionItems | CallActionItem[] |
language | string | null |
Interface: ConnectChannel
A number assigned to your application, as your application sees it.
Two things are deliberately withheld and will not appear later:
createdBy names a Connect operator, and the carrier-side record (which
carrier, which provider resource, what Connect pays per month) is Connect's
cost and Connect's credential surface. An application reads what was
assigned to it; it never provisions, prices or releases a number.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
kind | ChannelKind | - |
address | string | E.164 for a voice channel - the number a caller dials. |
label | string | null | - |
workspaceId | string | - |
status | ChannelStatus | - |
routing | ChannelRouting | null | null when nothing has been routed yet, and after a clearRouting(). |
handlerApplicationId | string | null | The application that answers this number's calls itself through Connect's carrier edge - yours, when your application has a telephony webhook configured at the time it claimed the number. null means Connect answers the number. |
Interface: ConnectContact
Someone a workspace knows how to reach - the telephony identity, not a
CRM record. See ContactsResource for why the difference decides whether
you reference this or copy it.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
primaryPhone | string | null | E.164, and immutable once set - the key this contact is identified by. null for a contact known only by email, which cannot be called: hide any call affordance rather than treating it as an error. The first phone such a contact gains becomes its primaryPhone. |
phones | ContactChannelValue[] | - |
emails | ContactChannelValue[] | - |
name | { display: string | null; given: string | null; family: string | null; } | - |
name.display | string | null | - |
name.given | string | null | - |
name.family | string | null | - |
business | { company: string | null; role: string | null; gstin: string | null; website: string | null; address: string | null; } | - |
business.company | string | null | - |
business.role | string | null | - |
business.gstin | string | null | - |
business.website | string | null | - |
business.address | string | null | - |
tags | string[] | - |
source | ContactSource | - |
createdAt | string | - |
updatedAt | string | - |
Interface: ConnectDataClassNotice
One kind of data your application holds, and what your policy said happens to it.
Properties
| Property | Type | Description |
|---|---|---|
key | string | Stable key, as declared in Console. Safe to switch on. |
label | string | What the customer was shown - e.g. "Call records". |
disposition | ConnectDataDisposition | - |
retainDays | number | null | How many days this class outlives the workspace in your own archive, or null when it goes with the purge. The one number in either deletion event you have to act on, and the reason policy is on the wire at all: purgeAfter is Connect's clock, this is yours. Read it rather than hardcoding the figure you typed into Console - that copy drifts the first time somebody edits the policy, and this one is the version the customer was actually promised. It is Connect's clamped value, so it is never longer than the platform ceiling even if your stored policy says otherwise. |
basis | string | null | The clause or rule you named as requiring it, shown to the customer verbatim. null for a class that is erased. |
Interface: ConnectDeletionPolicyNotice
Your application's own data policy, as it stood when this deletion was confirmed - the declaration the customer read before pressing the button.
Carried on both deletion events so your handler can be driven by the declaration rather than by a second copy of it in your code. Two things about it are worth relying on:
It is frozen. An operator editing the policy while a workspace is
already pending deletion does not change what that deletion runs under;
version is which one it is, so a deletion certificate can name it.
It is yours alone. A deletion fans out to every application the workspace subscribed to, and each one receives only its own policy.
Properties
| Property | Type | Description |
|---|---|---|
version | number | The policy version in force when the deletion was confirmed. |
fiduciaryRole | ConnectFiduciaryRole | - |
exportBeforePurge | boolean | Whether your policy promised the customer an export before the window closed. |
policyUrl | string | null | - |
dataClasses | ConnectDataClassNotice[] | Empty when your application has declared no policy - which the customer was shown as "this application hasn't told us", not as "nothing is kept". |
resourceReleases | ConnectResourceReleaseNotice[] | - |
Interface: ConnectDoNotCallActor
Who set or lifted a do-not-call entry.
Properties
| Property | Type | Description |
|---|---|---|
source | "application" | "portal" | - |
applicationId | string | null | The application the change came through. |
userId | string | null | A Connect user id - only when Connect verified it from a session token. |
externalActorId | string | null | Your own actor.id from a server-to-server write - your claim, not a Connect user. |
name | string | null | - |
Interface: ConnectDoNotCallChange
Properties
| Property | Type | Description |
|---|---|---|
changed | boolean | false for a repeat set, or a clear of a number not listed. |
status | ConnectDoNotCallStatus | - |
Interface: ConnectDoNotCallEvent
Properties
| Property | Type |
|---|---|
action | "set" | "cleared" |
reason | | ConnectDoNotCallReason | null |
note | string | null |
actor | ConnectDoNotCallActor |
at | string |
Interface: ConnectDoNotCallLookup
Properties
| Property | Type | Description |
|---|---|---|
status | ConnectDoNotCallStatus | - |
history | ConnectDoNotCallEvent[] | null | Newest first - only when asked for with history: true. |
Interface: ConnectDoNotCallStatus
A number's standing on a workspace's do-not-call list. Connect refuses
every outbound call to a listed number, so this is for showing the entry,
never for deciding whether to dial. A number never listed is
doNotCall: false with every other field null.
Properties
| Property | Type | Description |
|---|---|---|
phoneNumber | string | E.164, normalised. |
doNotCall | boolean | - |
reason | | ConnectDoNotCallReason | null | - |
note | string | null | - |
setAt | string | null | When they first asked, ISO-8601. A repeat set does not move it. |
setBy | ConnectDoNotCallActor | null | - |
clearedAt | string | null | The last time it was lifted, if it ever was. |
clearedBy | ConnectDoNotCallActor | null | - |
clearNote | string | null | - |
Interface: ConnectInvitation
Properties
| Property | Type | Description |
|---|---|---|
invitationId | string | - |
inviteeEmail | string | - |
expiresAt | string | ISO 8601. |
inviteUrl | string | Always present, so you can offer copy/share when the email did not go out. |
emailSent | boolean | - |
Interface: ConnectMemberAccess
Properties
| Property | Type |
|---|---|
capacity | ConnectMemberCapacity |
viewer | ConnectMemberViewer |
invitations | ConnectPendingInvitation[] |
Interface: ConnectMemberAccessResult
Properties
| Property | Type | Description |
|---|---|---|
userId | string | - |
roleKey | string | null | The role on your application after the change - null when access was revoked. |
granted | string[] | Your application's permissions this member now holds, re-resolved. |
Interface: ConnectMemberCapacity
How many member seats the workspace has, and whether there is another.
The number is the workspace's, not your plan's: a baseline plus
allowances.members from every subscription it holds, folded across
applications - so a seat bought through another product is a seat you may
fill. included: null means unlimited. used counts active members and
pending invitations, because an invitation is a seat already spoken for.
Advisory. The authoritative check runs inside invite, which is where two
people claiming the last seat at once is settled.
Properties
| Property | Type | Description |
|---|---|---|
included | number | null | - |
used | number | - |
canAdd | boolean | - |
reason | "exhausted" | "no_allowance" | "hard_ceiling" | null | no_allowance - nothing the workspace subscribes to grants member seats, and the answer is a plan. exhausted - every seat is taken, and the answer is an upgrade or a removal. They read alike and need different next steps, so word them differently. |
Interface: ConnectMemberViewer
What the signed-in caller may do with the roster, from their Connect permissions - not your application's.
Render your Invite and Change-role controls from this. Without it the only way to find out is to try, which is a button that exists to fail.
Properties
| Property | Type |
|---|---|
canInvite | boolean |
canManageAccess | boolean |
Interface: ConnectPendingInvitation
An invitation that has been sent and not yet accepted - and is holding a seat.
Properties
| Property | Type | Description |
|---|---|---|
invitationId | string | - |
inviteeEmail | string | - |
roleKey | string | null | The role on your application it carries, or null. |
expiresAt | string | ISO 8601. Seven days from when it was sent. |
Interface: ConnectResourceReleaseNotice
An identifier allocated to the workspace that must be held before reissue - a third clock, neither the grace window nor a retention floor.
Properties
| Property | Type | Description |
|---|---|---|
key | string | - |
label | string | - |
quarantineDays | number | Days to hold it before handing it to somebody else. |
Interface: ConnectScreen
One screen in your application, described for the assistant.
Why you declare these rather than Connect discovering them
Connect has no idea what your app is. It cannot know that /deals is a
pipeline, that a "lead" becomes a "contact", or that the thing your users
call a quote lives under Settings. Without that, an assistant sitting inside
your product answers from general knowledge about software - which is exactly
how it reads.
Declaring a screen is the cheapest possible fix: one line of prose per route, and questions like "what can I do here?" start answering in your product's own words.
What Connect does and does not do with them
They are prose and links, and nothing else. Connect puts your
descriptions into the model's context so it can answer and so showActions
can link to your screens. It never uses them to decide what anyone may do -
your app's own authorization is untouched and unconsulted here - and it never
executes anything on your behalf.
They also arrive from a browser, so Connect treats them as untrusted text: bounded in count and length, and flattened to single lines before they reach a prompt.
Properties
| Property | Type | Description |
|---|---|---|
path | string | The route, as your router writes it. Dynamic segments in brackets - /deals/[dealId] - so a live path with a real id still matches. |
purpose | string | One line: what a person does here, in your product's words. |
suggestions? | string[] | Opening questions offered as chips on an empty conversation, phrased as your users would type them. Two or three; a fourth is a menu. |
Interface: ConnectTaskDelivery<TPayload>
What Cloud Tasks POSTs to a queue's URL - and what verifyConnectTask returns.
Type Parameters
| Type Parameter | Default type |
|---|---|
TPayload | unknown |
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | The workspace the enqueue was gated on - trust this, not anything in payload. |
queue | string | - |
payload | TPayload | - |
retryCount | number | From Cloud Tasks' own headers: 0 on the first attempt. |
taskName | string | null | Cloud Tasks' task id, stable across retries. |
Interface: ConnectTokenClaims
The claims of a Connect application token (typ: connect-app+jwt, ES256).
Properties
| Property | Type | Description |
|---|---|---|
iss | string | Connect's origin. |
aud | string | The provider's exposedApi.audience. |
sub | string | The consumer application id (app-only) or the member's user id (delegated). |
act? | { sub: string; } | Present only on a delegated token: the application acting for sub. |
act.sub | string | - |
azp | string | The consumer application id - always present. |
wid | string | Connect workspace id. |
scope | string | Space-separated. |
jti | string | - |
iat | number | - |
exp | number | - |
Interface: ConnectTokenResponse
POST /oauth/token's 200, exactly as the route answers it - RFC 6749 §5.1,
snake_case. connect.applications.token() translates it to
ConnectAccessToken.
Properties
| Property | Type |
|---|---|
access_token | string |
token_type | "Bearer" |
expires_in | number |
scope | string |
issued_token_type? | "urn:ietf:params:oauth:token-type:access_token" |
Interface: ConnectUser
Wire types for the /api/v1/application/* surface - the application-partner API,
and the only thing this SDK covers.
Hand-written rather than generated so the published package carries no
dependency on Connect's internals. src/conformance.test.ts type-checks
these against the server's own DTOs, so drift fails the build here instead
of surfacing as a runtime surprise in a partner's integration.
Dates cross the wire as ISO 8601 strings and are left that way - parsing
them into Date would be a lossy guess about what the caller wants, and
new Date(value) is right there.
Properties
| Property | Type | Description |
|---|---|---|
userId | string | - |
email | string | null | - |
emailVerified | boolean | - |
displayName | string | null | - |
photoUrl | string | null | The member's profile picture, or null when they have not set one. Deliberately nullable rather than a generated placeholder: only you know what your fallback should look like. <ConnectUserMenu> renders initials via <Avatar.Fallback>, which also covers the URL loading slowly or failing outright - so pair the two rather than branching on null. |
workspaceId | string | null | The workspace this session is scoped to - the tenant to key your own data on. null only for a Connect platform staff session, where staff is true; see SessionAccess. Nullable since 0.11.0, and the breaking change in that release. If you do not turn staff access on for your application (it is off by default and per-application), this is never null and the only work is a type narrow. If you do, decide first what your tenanting does with a session that has no tenant - typically: serve your operator console off staff, and nothing else. |
workspaceName | string | null | null on a staff session, for the same reason as workspaceId. |
workspaceType | WorkspaceType | null | Whether this is a one-seat personal account or a team. The one fact about the signed-in workspace you cannot look up yourself - the application API has no workspace read - so branch on it rather than inferring an account's shape from whether it happens to have a second member. An "individual" workspace has exactly one seat and can never have another, so a members table, an invite button or a seat-count meter is dead UI for it. Re-read it from identity.getUser(); do not cache it past the sign-in. An "individual" workspace can convert to "business" under a live session (the reverse never happens). null on a staff session - there is no workspace to have a type. |
Interface: ConnectUserSession
One login to the Connect account behind the sessionToken you called with -
not scoped to this application. A Connect session is one browser's login to
Connect itself, and it fans out to every partner application's own session -
Connect has no per-application session concept to filter by. sessions.list()
returns every one of them, exactly like Connect's own account settings
page does for the user themselves.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
createdAt | string | ISO 8601. |
lastActiveAt | string | ISO 8601. |
expiresAt | string | ISO 8601. |
device | SessionDevice | - |
ip | string | null | - |
ipCity | string | null | - |
ipCountry | string | null | - |
isCurrent | boolean | Whether this is the session your sessionToken was minted from. |
Interface: ConnectWebhookEnvelope<T>
The envelope, as it arrives on the wire.
Type Parameters
| Type Parameter | Default type |
|---|---|
T extends ConnectWebhookEventType | ConnectWebhookEventType |
Properties
| Property | Type | Description |
|---|---|---|
id | string | Unique per delivery. Dedupe on this. Delivery is at-least-once and unordered: a partner that assumes exactly-once, or assumes order, will eventually be wrong. |
type | T | - |
createdAt | string | ISO-8601. |
apiVersion | string | Bumped only for a breaking payload change - never for a new event type. |
data | ConnectWebhookEventMap[T] | - |
Interface: ConnectWebhookEventMap
Maps each event type to the shape of its data.
Properties
| Property | Type | Description |
|---|---|---|
workspace.deletion_scheduled | WorkspaceDeletionScheduledEvent | - |
workspace.deletion_cancelled | WorkspaceDeletionCancelledEvent | - |
workspace.deleted | WorkspaceDeletedEvent | - |
subscription.cancelled | SubscriptionCancelledEvent | - |
subscription.activated | SubscriptionActivatedEvent | - |
call.completed | CallCompletedEvent | - |
call.missed | CallMissedEvent | - |
call.analyzed | CallAnalyzedEvent | - |
booking.confirmed | BookingConfirmedEvent | - |
booking.cancelled | BookingCancelledEvent | - |
booking.rescheduled | BookingRescheduledEvent | - |
contact.created | ContactCreatedEvent | - |
contact.updated | ContactUpdatedEvent | - |
application.event | ApplicationEventEnvelope | Another application published an event to this workspace and your connection to it holds the event's requiredScope. data.type is the publisher's own event type; verifyApplicationEvent returns it typed. |
Interface: ConnectWorkspaceMember
One member of the workspace a session is on.
granted is resolved for your application - permission ids from your own
catalog, not the member's Connect roles.
Properties
| Property | Type | Description |
|---|---|---|
memberId | string | The id the calendar speaks in - Booking.hostMemberIds holds these, and so does listBookings' hostMemberId filter. Carried beside userId because the two are different identifiers and scheduling is the one surface that uses this one: joining a diary to a roster, filtering it to one host, or putting a name against an appointment all go through memberId. Everywhere else a member is a userId. Opaque and workspace-scoped. Do not derive it from userId or address a member by it elsewhere in this API. |
userId | string | - |
assigned | boolean | Whether this member holds a seat on your application. Only assigned members can sign in to it; unassigned rows appear only when you asked for them with members({ includeUnassigned: true }), to assign from. |
email | string | - |
displayName | string | null | - |
photoUrl | string | null | - |
status | string | - |
granted | string[] | - |
role | string | null | - |
Interface: ConnectWorkspaceProfile
One subscribing workspace's identity, read from your server with nobody
signed in - workspaces.get. Needs the workspace-profile:read scope.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
name | string | What the workspace calls itself - the name a greeting says. |
legalName | string | null | The self-declared billing name, or null until the customer has needed an invoice. |
type | WorkspaceType | - |
status | "active" | "suspended" | "pending_deletion" | "deleted" | - |
industry | string | null | What the customer said they do, from Connect's Workspace settings - coarse and self-declared ("E-commerce & Retail"), null when never answered. A hint to pre-select from, so you do not ask again; not a classification to decide on. Absent (read as null) from a Connect deployment older than SDK 0.65. |
website | string | null | The website the customer gave under Workspace settings, as typed, or null. Self-declared - a place to start reading, not proof of ownership. Read as null from a Connect deployment older than SDK 0.67. |
owner | { displayName: string | null; email: string | null; } | - |
owner.displayName | string | null | - |
owner.email | string | null | The owner's sign-in email. |
Interface: ConsumeAddonInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
addonId | string | - |
quantity | number | Whole units of the add-on's unit. |
description? | string | - |
Interface: ConsumeAddonResponse
Properties
| Property | Type | Description |
|---|---|---|
addonId | string | - |
consumed | number | - |
balance | number | Remaining immediately after this call. Negative only when overagePolicy is "bill". |
overageUnits | number | - |
Interface: ConsumeCreditInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
amount | number | Paise, pre-tax. |
idempotencyKey | string | Required - your own id for the unit of work. A retry under it moves nothing. |
description | string | Shown to the customer on their ledger. |
reference? | string | null | - |
Interface: ConsumedApplicationProvider
One application your application declares it consumes, with the audience a
token for it names as resource. audience: null is a declaration with
nothing behind it - the provider no longer exists or exposes no API.
Properties
| Property | Type | Description |
|---|---|---|
application | string | The provider's application key, e.g. "products". |
audience | string | null | - |
Interface: ContactChannelValue
A phone or email on a contact.
verified means a human confirmed it reaches them - only a manual source
may ever set it. Branch on it: an unverified value is one an extraction
heard on a call and nobody has checked since.
Properties
| Property | Type |
|---|---|
value | string |
label | string | null |
verified | boolean |
Interface: ContactCreatedEvent
Connect learned about a person.
The event with no equivalent anywhere else in this API: most often an agent
captured them mid-call through the contact-capture skill, so the contact
exists in Connect and your product has never heard of them. Without this the
capture is invisible where the customer actually works.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
contactId | string | - |
name | string | null | - |
phone | string | null | E.164. |
email | string | null | - |
company | string | null | - |
source | ConnectContactSource | - |
occurredAt | string | ISO-8601. |
Interface: ContactFactsInput
What an interaction learned about a person, for contacts.upsertFromInteraction.
Every key optional: an absent key says nothing, an explicit null says
"unknown" - and neither clears a value a person typed, because automated
facts only fill blanks. verified on a phone or email is ignored; only a
human can verify one.
Properties
| Property | Type | Description |
|---|---|---|
name? | { display?: string | null; given?: string | null; family?: string | null; } | - |
name.display? | string | null | - |
name.given? | string | null | - |
name.family? | string | null | - |
business? | { company?: string | null; role?: string | null; gstin?: string | null; website?: string | null; address?: string | null; } | - |
business.company? | string | null | - |
business.role? | string | null | - |
business.gstin? | string | null | - |
business.website? | string | null | - |
business.address? | string | null | - |
phones? | { value: string; label?: string | null; verified?: boolean; }[] | E.164 values. Added to the contact's list, never replacing it. |
emails? | { value: string; label?: string | null; verified?: boolean; }[] | - |
tags? | string[] | - |
notes? | string | null | - |
Interface: ContactInteractionResult
Properties
| Property | Type | Description |
|---|---|---|
contact | ConnectContact | - |
created | boolean | Whether this write created the contact. |
changedFields | string[] | Dotted paths that actually changed - what landed, not what you sent. Empty when nothing did. |
match | ContactUpsertMatch | Which key found the contact: phone, email, or created when neither did. |
conflict | boolean | The phone matched one contact and the email matched another. The facts went to the phone match and the email stayed with its owner - Connect never merges two people silently. Show it to a person. |
Interface: ContactUpdatedEvent
A known contact's details changed.
changed names the fields that actually moved, so you can apply a patch
rather than overwriting fields your own users may have curated - a customer
who corrected a company name in your product should not have it replaced by
what an agent heard over a bad line.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
contactId | string | - |
name | string | null | - |
phone | string | null | E.164. |
email | string | null | - |
company | string | null | - |
changed | string[] | The field names that changed on this update. |
source | ConnectContactSource | - |
occurredAt | string | ISO-8601. |
Interface: CreditBalance
A workspace's prepaid credit with your application - see CreditResource.
Every amount is paise, pre-tax.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
applicationId | string | - |
label | string | What the customer sees the balance as - "Calling credit". |
currency | "INR" | - |
balance | number | Negative only while an overdraft Connect granted you is in use. |
spendable | number | What consume may take right now: balance plus any overdraft. |
overdraftLimit | number | - |
toppedUp | number | - |
consumed | number | - |
refunded | number | - |
topUpAvailable | boolean | false while your credit program is switched off - existing credit can still be spent. |
topUpPath | string | Pass to useConnect().openConnect() to send the customer to buy more. |
updatedAt | string | null | ISO 8601. |
Interface: CreditLedgerEntry
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
type | CreditEntryType | - |
amount | number | Paise, signed: positive adds credit, negative spends it. |
balanceAfter | number | - |
idempotencyKey | string | - |
description | string | - |
reference | string | null | - |
reversesEntryId | string | null | On a refund: the consume entry it credited back. |
createdAt | string | ISO 8601. |
Interface: CreditLedgerPage
Properties
| Property | Type |
|---|---|
items | CreditLedgerEntry[] |
nextCursor | string | null |
Interface: CreditMovement
Properties
| Property | Type | Description |
|---|---|---|
duplicate | boolean | true when this idempotency key had already been applied - nothing moved this time. |
entry | CreditLedgerEntry | - |
balance | number | - |
spendable | number | - |
Interface: DesignSystem
Properties
| Property | Type | Description |
|---|---|---|
radiusScale | ScaleStep[] | - |
spacingScale | ScaleStep[] | - |
shadows | string[] | Raw box-shadow values, most used first. |
breakpoints | string[] | @media breakpoint widths as written, ascending. |
customProperties | { name: string; value: string; }[] | Every --* declaration on :root - the company's own token vocabulary, verbatim. |
framework | CssFramework | null | - |
Interface: DisconnectIntegrationResponse
Properties
| Property | Type |
|---|---|
success | true |
Interface: DriveFile
A file your application uploaded - see connect.drive.list.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
fileName | string | - |
mimeType | string | - |
size | number | - |
uploadedAt | string | ISO-8601. |
Interface: DriveFileList
Properties
| Property | Type | Description |
|---|---|---|
files | DriveFile[] | Newest first. |
truncated | boolean | More files exist than the limit returned. There is no cursor. |
Interface: DriveUsage
How full a workspace's Drive is. The limit is the one an upload is refused
against, so a meter built on this agrees with quota_exceeded.
Properties
| Property | Type | Description |
|---|---|---|
usedBytes | number | - |
limitBytes | number | null | null = unlimited. |
Interface: EmitEventInput
One event you push into Connect's analytics pipeline.
Properties
| Property | Type | Description |
|---|---|---|
name | string | Dot-namespaced and lowercase, e.g. checkout.completed. Prefixes Connect emits itself - api., billing., addon., subscription., connect. - are rejected, so your events can never inflate a metered number. |
occurredAt? | string | Date | Defaults to now. Clamped server-side to within 48 hours. |
sessionToken? | string | Attributes the event to the signed-in user and their workspace. Takes precedence over workspaceId; if both are sent they must name the same workspace, or the batch is refused (422). |
workspaceId? | string | The workspace a server-side emit is about - what to send when there is no signed-in user to pass a sessionToken for (a cron, a webhook, a use-case). Verified, not believed: Connect accepts it only if your application is installed in that workspace (a live subscription), and refuses the whole batch with a 403 otherwise - which emit reports as delivered: false. Checked once per distinct workspace in a batch. |
eventId? | string | Your idempotency key for this event, e.g. deal.won:<dealId>, so a retry or a replay counts once. Up to 64 characters of letters, digits and . _ : -. Unique within your application only - Connect namespaces it by application, so another application's order:1 is a different event. |
subjectId? | string | Who the event is about when that is not a signed-in user - a caller, a ticket requester, a message recipient - so a distinct metric can count them from a server-side emit. Your own pseudonymous key (a keyed hash such as HMAC-SHA256 of a phone number, or an opaque record id): never a raw phone number or email, and not a bare hash of one, which is reversible by enumeration. Same alphabet and length as eventId. Takes precedence over the sessionToken's user for counting; attribution to the workspace is unchanged. |
dimensions? | Record<string, string> | Declared dimension values to break this event down by, e.g. { ownerId: "u_1", stage: "won" }. At most two per event, each key declared in your application's analytics schema (an undeclared key refuses the batch with a 422), each value 1-100 characters. Values past a dimension's maxCardinality fold into "_other" in the rollup. |
value? | number | A whole number in the smallest unit of unit - paise, bytes, milliseconds. |
unit? | string | - |
props? | Record<string, unknown> | - |
Interface: EmitEventsResponse
Properties
| Property | Type | Description |
|---|---|---|
delivered | boolean | False when the call failed outright - see AnalyticsResource.emit. |
accepted | number | - |
attributed | number | Events tied to a workspace or a user - by a resolved sessionToken or a verified workspaceId. |
Interface: EnqueueTaskInput
One task, onto one of this application's declared task queues, for one
workspace it serves. Hand-written like every wire type here;
conformance.test.ts holds it to EnqueueApplicationTaskInputSchema.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
queue | string | A queue declared for this application in Console. |
payload? | unknown | Any JSON. Delivered back as payload. Keep it to ids - 100 KB at most. |
name? | string | A dedup key. A second enqueue with the same name for the same workspace answers duplicate and queues nothing - name a self-chaining step after the step (campaign-42-page-3) so two runners cannot both queue it. |
scheduleAt? | string | ISO-8601, at most 30 days ahead. Omit to run as soon as the queue's rate allows. |
Interface: EnqueueTaskResponse
Properties
| Property | Type | Description |
|---|---|---|
status | "queued" | "duplicate" | "unavailable" | unavailable means this Connect deployment has no task queue (local development). Nothing was queued; fall back to whatever you would do without one. |
scheduleAt | string | null | - |
Interface: EnrichedAddress
Every field nullable, including the ones your own record calls required.
An enrichment is partial by nature: a site with no address printed on it yields no address. A shape that demanded a city would only be forcing one to be invented.
Properties
| Property | Type | Description |
|---|---|---|
line1 | string | null | - |
line2 | string | null | - |
city | string | null | - |
state | string | null | The state or region's name as written, not a code. |
postalCode | string | null | - |
country | string | null | ISO 3166-1 alpha-2, uppercase. |
Interface: EnrichedCompany
What Connect read about a company, and where each part came from.
Every value is a claim from a page named in citations. Nothing here is
recalled by a model: a field the pages did not state is null, which is why
null is common and should be rendered as "not found" rather than as an
empty input somebody has to notice.
Properties
| Property | Type | Description |
|---|---|---|
domain | string | Normalised - lowercase, no scheme, no www.. |
name | string | null | - |
legalName | string | null | The registered entity name. Always null today. It is reserved for a statutory register, and deliberately never filled from a website - a footer says "Acme" where the register says "Acme Industries Private Limited", and this is the one field here that would end up on a contract. |
description | string | null | - |
industry | string | null | - |
sizeBand | CompanySizeBand | null | - |
phone | string | null | - |
email | string | null | - |
website | string | null | - |
address | EnrichedAddress | null | - |
confidence | "medium" | "low" | "high" | Derived from how many fields the pages actually supported - not the model's opinion of itself, which reflects the confidence of the prose it read. Treat low as "offer this", high as "safe to pre-fill". |
citations | EnrichmentCitation[] | - |
retrievedAt | string | ISO-8601. Age the profile against this. |
Interface: EnrichmentCitation
Properties
| Property | Type | Description |
|---|---|---|
source | EnrichmentSource | - |
url | string | null | The exact page read. null only for domain. |
retrievedAt | string | ISO-8601. |
Interface: ExecuteIntegrationActionResponse
Properties
| Property | Type |
|---|---|
data | unknown |
successful | boolean |
Interface: ExposedApi
What a provider application offers other applications - set in Console, never by a deploy.
Properties
| Property | Type | Description |
|---|---|---|
audience | string | The token's aud - and the resource a consumer names when asking for one. |
scopes | ExposedApiScope[] | - |
events | ExposedApiEvent[] | - |
Interface: ExposedApiEvent
One event type a provider application publishes through Connect.
Properties
| Property | Type | Description |
|---|---|---|
type | string | <appKey>.<noun>.<verb>, e.g. messaging.chat.lead_captured. |
description | string | - |
requiredScope | string | A consumer receives the event only while its connection holds this scope. |
Interface: ExposedApiScope
One scope a provider application offers, as listed on its exposedApi.
Properties
| Property | Type | Description |
|---|---|---|
id | string | <appKey>:<resource>.<verb>, e.g. messaging:chat.read. |
description | string | Shown verbatim on the consent screen. |
delegable | boolean | May a token acting for a member (tokenForMember) carry this scope? |
Interface: FormatProductPriceOptions
Properties
| Property | Type | Description |
|---|---|---|
includeInterval? | boolean | Append the recurring interval ("/ month"). Default true. |
includeUnit? | boolean | Append the unit ("/ hour"). Default true. |
onRequestLabel? | string | What on_request (or a priced model with no amount) reads as. Default "Price on request". |
Interface: GrievanceChannel
The platform's DPDP grievance channel - companyProfile.grievance.
Flat and non-nullable: Connect resolves every field against its published
defaults before answering, so there is no "not configured yet" state to
branch on here. See PlatformResource.getCompanyProfile.
Properties
| Property | Type | Description |
|---|---|---|
officerName | string | - |
officerEmail | string | - |
hours | string | Free text, e.g. "Monday - Friday, 10:00 AM - 6:00 PM IST". |
responseDays | number | The redressal window the published notice commits to, in days. |
Interface: IdTokenClaims
Verified claims from an idToken.
Indexable
[claim: string]: unknownProperties
| Property | Type | Description |
|---|---|---|
sub | string | Connect user id. |
aud | string | Your client_id. |
iss | string | - |
exp | number | - |
iat | number | - |
org_id | string | - |
email? | string | null | - |
email_verified? | boolean | - |
Interface: InitiateIntegrationConnectionResponse
The connection isn't active yet when this returns - see IntegrationsResource.initiateConnect's doc comment.
Properties
| Property | Type | Description |
|---|---|---|
redirectUrl | string | Send the browser here to complete the provider's OAuth screen. |
connectionId | string | - |
Interface: IntegrationsListResponse
Properties
| Property | Type |
|---|---|
toolkits | IntegrationToolkit[] |
Interface: IntegrationToolkit
A Composio-hosted third-party toolkit (Google Calendar, Slack, Twitter/X, ...) this workspace can see - only ever one a platform admin enabled in Connect's own Console, joined with this workspace's own connection status. Connections are workspace-shared: one covers every member, the way a Slack app install is shared rather than per-user.
Properties
| Property | Type |
|---|---|
slug | string |
name | string |
description | string |
category | string | null |
logoUrl | string | null |
status | IntegrationConnectionStatus |
Interface: KvEntry
One entry from the kv store.
Properties
| Property | Type | Description |
|---|---|---|
key | string | - |
value | unknown | - |
expiresAt | string | null | ISO-8601, or null when the entry never expires. |
updatedAt | string | - |
Interface: KvPage
Properties
| Property | Type | Description |
|---|---|---|
items | KvEntry[] | - |
nextCursor | string | null | Pass as the next cursor. null is the end of the namespace. A page can be shorter than limit and still have one - expired entries are filtered after paging - so this, not an empty items, is the signal to stop. |
Interface: ListApplicationConnectionsResponse
GET /connections's 200.
Properties
| Property | Type |
|---|---|
connections | ApplicationConnectionSummary[] |
Interface: ListBookingsPage
Properties
| Property | Type | Description |
|---|---|---|
bookings | Booking[] | - |
nextCursor | string | null | Pass back as cursor. A cursor belongs to one set of filters. |
hasMore | boolean | - |
totalCount | number | - |
Interface: ListConsumedApplicationProvidersResponse
GET /connections/providers's 200 - only your own declarations, never another application's.
Properties
| Property | Type |
|---|---|
providers | ConsumedApplicationProvider[] |
Interface: ListSendingDomainsResponse
GET /email/domains's 200.
Properties
| Property | Type |
|---|---|
domains | SendingDomain[] |
Interface: ManageableEventType
One appointment type, as a management UI sees it - richer than
BookableEventType's booking-catalogue shape, which deliberately
withholds location.value and everything else here. Fetched from
listManageableEventTypes, never the booking flow's listEventTypes.
host collapses a group host to {kind:"group"} with nothing else -
there is no groups read in this API, so a team host is representable only
as "not this, edit it in Connect".
Properties
| Property | Type |
|---|---|
id | string |
slug | string |
name | string |
description | string | null |
durationMinutes | number |
bufferBeforeMinutes | number |
bufferAfterMinutes | number |
host | | { kind: "member"; memberId: string; } | { kind: "group"; } |
scheduleId | string |
location | { kind: "phone" | "address" | "link"; value: string | null; } |
location.kind | "phone" | "address" | "link" |
location.value | string | null |
active | boolean |
publicBookingEnabled | boolean |
Interface: MetricDelta
How a figure moved against its comparison - what ConnectStatTile's delta pill draws.
Properties
| Property | Type | Description |
|---|---|---|
percent | number | null | Whole percent. null when the previous figure was zero - a percentage of nothing is undefined, not 100. |
direction | "flat" | "up" | "down" | - |
Interface: Offering
Properties
| Property | Type | Description |
|---|---|---|
name | string | - |
category | string | null | The company's own grouping when it has one. |
description | string | null | - |
url | string | null | - |
Interface: PaletteEntry
Properties
| Property | Type | Description |
|---|---|---|
hex | string | Lowercase #rrggbb. |
role | PaletteRole | null | Assigned by a model over measured values - never invented by one. |
source | PaletteSource | - |
variableName | string | null | The CSS custom property it came from, when source is css-var. |
occurrences | number | - |
Interface: Plan
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
planGroup | string | The tier this plan prices, independent of how often it bills. A tier's monthly and annual plans share one - so "growth" covers both the "Growth" monthly row and the "Growth" annual row. This is what ConnectPlans groups its cards by. Before it existed the grid paired plans by matching name, which Connect neither enforces to be unique nor keeps stable across a rename or a clone - so a catalog could silently split one tier across two cards, or show a saving computed from two plans that had nothing to do with each other. |
description | string | - |
price | number | GST-exclusive, in paise. |
currency | "INR" | - |
billingInterval | BillingInterval | - |
gstRate | number | - |
addons | PlanAddon[] | - |
seats | PlanSeats | - |
storage | PlanStorage | - |
trialDays | number | null | - |
features | PlanFeature[] | What this plan unlocks, labelled for display - see PlanFeature. |
limits | ResolvedPlanLimit[] | How much of each metered resource this plan allows, labelled for display - see ResolvedPlanLimit. |
Interface: PlanAddon
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
price | number | GST-exclusive, in paise. Per plan cycle, or per year when billingCadence is "annual". |
consumable? | PlanAddonConsumable | null | - |
billingCadence? | "plan" | "annual" | null | When the add-on is billed. "annual" = once on the first cycle that holds it, then on each anniversary - on a monthly plan too - so render its price as "/year". "plan" (or absent, from a Connect that predates the field) = every plan cycle, as before. |
Interface: PlanAddonConsumable
Properties
| Property | Type |
|---|---|
unit | string |
includedPerCycle | number |
carryForward | boolean |
overagePolicy | "reject" | "bill" |
overagePricePerUnit | number | null |
maxOverageUnits | number | null |
Interface: PlanFeature
A plan feature with its label, as the catalogue read reports it.
Note the asymmetry with SubscribedPlan.features, which is bare
string[] and stays that way. That field is a gating contract - your
backend calls hasFeature(workspaceId, "campaign-module") against it, and
ConnectFeatureGate matches on it - so turning it into objects would break
every check for a label nobody gates on. This one exists to be rendered,
and rendering a bare key puts "campaign-module" on a pricing card where it
meant "Campaign module".
key is carried here too, so a component doing both needs no second lookup.
Properties
| Property | Type | Description |
|---|---|---|
key | string | The same value SubscribedPlan.features holds and hasFeature gates on. |
name | string | Human label. Falls back to key for a plan predating the application's feature catalog. |
description | string | Empty string when the catalog carries no description. |
Interface: PlanLimit
What a plan allows of one metered resource - the gating shape, which is
what getLimit returns and planLimitAllows takes.
Three states rather than a nullable number, deliberately. The obvious
encoding - a number | null where null means unlimited - makes
if (!limit) refuse() read as correct while refusing every subscriber on
the most generous tier, which is the one case nobody tests. Use
planLimitAllows rather than comparing by hand: count < value is off by
one when adding more than one row, and value ?? Infinity grants unlimited
to a plan that allows none.
Properties
| Property | Type | Description |
|---|---|---|
model | "unlimited" | "none" | "limited" | - "none": this plan does not allow the resource at all. Also what a key the plan carries no answer for reads as - see SubscribedPlan.limits. - "limited": a ceiling of value. - "unlimited": no ceiling. |
value | number | null | The ceiling. Non-null iff model is "limited", and always >= 1 there - zero is spelled "none". |
Interface: PlanSeats
Properties
| Property | Type |
|---|---|
model | "unlimited" | "fixed" | "per-seat" |
included | number | null |
pricePerExtraSeat | number | null |
Interface: PlansResponse
Properties
| Property | Type |
|---|---|
plans | Plan[] |
Interface: PlanStorage
Properties
| Property | Type |
|---|---|
model | "unlimited" | "fixed" |
includedBytes | number | null |
Interface: PresenceCategoryScore
Properties
| Property | Type | Description |
|---|---|---|
category | PresenceCategory | - |
score | number | 0-100 within the category, so a weak one is legible beside a strong one. |
weight | number | - |
earned | number | - |
Interface: PresenceCriterion
One check, its verdict and the evidence behind it.
weight and earned are both published so the total can be reconstructed:
a partial that cost two points and one that cost six look identical
without them, and a score nobody can reconstruct is a score nobody trusts.
Properties
| Property | Type | Description |
|---|---|---|
key | string | Stable across scans, so two reports of one domain can be diffed. |
category | PresenceCategory | - |
label | string | - |
status | PresenceCriterionStatus | - |
weight | number | The check's full weight in the rubric. |
earned | number | The share of weight actually earned. |
observed | string | What was seen, in one line. |
citation | string | null | The URL that settles it, when there is one. |
fix | string | null | What to change. null when the check passed. |
Interface: PresenceRecommendation
One prioritised thing to do. fix is Connect's own reviewed text; why is written for this business.
Properties
| Property | Type | Description |
|---|---|---|
criterionKey | string | Names the check this came from, so advice is always traceable to evidence. |
category | PresenceCategory | - |
priority | number | 1 is what to do first. Always 1..n with no gaps. |
title | string | - |
why | string | What it costs this business specifically. Empty when the narrative could not be generated. |
fix | string | - |
Interface: PresenceScan
How visible a business is online.
The important property, and the one that makes this worth showing a
customer: the score was computed from the checks, and the checks were
measured. Every point comes from something a fetcher observed on the site.
A model contributed only the ordering of recommendations and the prose in
why and summary; it cannot alter a status, a weight or the score.
Properties
| Property | Type | Description |
|---|---|---|
domain | string | Normalised - lowercase, no scheme, no www.. |
score | number | 0-100. |
band | PresenceBand | - |
categories | PresenceCategoryScore[] | - |
criteria | PresenceCriterion[] | Every check, passed and failed. The audit trail behind the number. |
social | PresenceSocialProfile[] | Every profile found, including networks the rubric does not weight individually. |
summary | string | Two or three sentences on the overall state. Empty when it could not be generated - the scan is complete without it. |
recommendations | PresenceRecommendation[] | What to do, in order. Render this before criteria. |
scannedAt | string | ISO-8601. Age the scan against this. |
Interface: PresenceSocialProfile
A profile the subject's own site linked.
Claimed, never verified. Nothing in a scan proves the account belongs to that business - that needs a credential for each network. Render it as "linked from their site", never as a confirmed account.
Properties
| Property | Type | Description |
|---|---|---|
network | PresenceNetwork | - |
url | string | - |
handle | string | null | - |
machineReadable | boolean | Whether the site declared it in schema.org sameAs markup rather than only as a link. The one to read closely. false means a real presence with none of the discoverability - a search engine has to infer the connection from a footer icon - and it is among the most common findings a scan produces. |
Interface: Product
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
kind | ProductKind | - |
status | ProductStatus | - |
name | string | ≤ 160. |
sku | string | null | ≤ 64, unique among non-archived when set. |
summary | string | null | One line, ≤ 280 - cards, pickers. |
description | string | null | Markdown, ≤ 20,000. |
pricing | ProductPricing | - |
images | ProductImage[] | ≤ 12; images[0] is the cover; order is the gallery. |
tags | string[] | ≤ 20, trimmed, case-insensitively unique, ≤ 40 chars each. |
fields | Record<string, ProductFieldValue> | Custom field values by field key. |
externalIds | ProductExternalIds | Links to other systems' copies; set by importers. |
bundle | ProductBundle | null | null for an ordinary product. |
version | number | Optimistic concurrency - every write sends the version it read. |
createdAt | string | - |
createdBy | string | "user:<uid>" or "app:<key>". |
updatedAt | string | - |
updatedBy | string | - |
archivedAt | string | null | - |
archivedBy | string | null | - |
Interface: ProductBundle
A product made of other products. 2 to 20 components, no duplicates, none
a bundle itself. Products derives the bundle's pricing.taxRatePercent and
pricing.hsnSac from the components on every save (deriveBundleTax), so
a host that only reads pricing - a quote line, an invoice - needs no
bundle knowledge at all. Rate and HSN/SAC sent on a bundle are overwritten.
Properties
| Property | Type |
|---|---|
supply | ProductBundleSupply |
components | ProductBundleComponent[] |
Interface: ProductBundleComponent
Extended by
Properties
| Property | Type | Description |
|---|---|---|
productId | string | - |
quantity | number | A whole number, 1 or more. |
Interface: ProductBundleComponentSummary
A component as a read returns it - enough to draw the row with no extra call.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
sku | string | null | - |
kind | ProductKind | - |
status | ProductStatus | An archived component stays in its bundle; show it flagged. |
coverThumbUrl | string | null | The component's cover thumbnail, or null. |
taxRatePercent | number | null | - |
hsnSac | string | null | - |
Interface: ProductBundleComponentView
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
productId | string | - | ProductBundleComponent.productId |
quantity | number | A whole number, 1 or more. | ProductBundleComponent.quantity |
product | | ProductBundleComponentSummary | null | null only if the component cannot be read - nothing is deleted, so this should not happen. | - |
Interface: ProductBundleView
ProductBundle with each component resolved - ProductView.bundle.
Properties
| Property | Type |
|---|---|
supply | ProductBundleSupply |
components | ProductBundleComponentView[] |
Interface: ProductExternalIds
Properties
| Property | Type |
|---|---|
crmPriceListItemId? | string |
zohoBooksItemId? | string |
Interface: ProductField
Properties
| Property | Type | Description |
|---|---|---|
key | string | Immutable slug - the document id. |
label | string | - |
type | ProductFieldType | Immutable once a product holds a value. |
options | ProductFieldOption[] | select / multi_select. |
required | boolean | Binds only on the kinds the field applies to. |
description | string | null | - |
appliesTo | ProductFieldAppliesTo | Products, services or both. A form for the other kind does not offer it. |
order | number | - |
archived | boolean | Fields archive too - values stay on products. |
Interface: ProductFieldOption
Properties
| Property | Type |
|---|---|
value | string |
label | string |
Interface: ProductFieldsResponse
Properties
| Property | Type |
|---|---|
items | ProductField[] |
Interface: ProductImage
Extended by
Properties
| Property | Type | Description |
|---|---|---|
fileId | string | Connect Drive file id. |
alt | string | null | - |
Interface: ProductImageUploadResponse
POST /images - upload first, then reference fileId in images.
Properties
| Property | Type |
|---|---|
fileId | string |
url | string |
Interface: ProductImageView
An image as a read returns it - with URLs that are safe in an <img> on any origin and carry no session.
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
fileId | string | Connect Drive file id. | ProductImage.fileId |
alt | string | null | - | ProductImage.alt |
url | string | - | - |
thumbUrl | string | - | - |
Interface: ProductInput
The writable fields. status: "archived" is never accepted - archiving is
its own verb (POST /products/{id}/archive) so it is audited as one.
Properties
| Property | Type | Description |
|---|---|---|
kind | ProductKind | - |
status | "active" | "draft" | - |
name | string | - |
sku | string | null | - |
summary | string | null | - |
description | string | null | - |
pricing | ProductPricing | - |
images | ProductImage[] | - |
tags | string[] | - |
fields | Record<string, ProductFieldValue> | - |
bundle | ProductBundle | null | null for an ordinary product. A PATCH replaces it whole; null makes a bundle ordinary again. |
Interface: ProductListParams
GET /products's query.
Properties
| Property | Type | Description |
|---|---|---|
q? | string | - |
kind? | ProductKind | - |
status? | "all" | ProductStatus | Defaults to active; "all" includes every status. |
tags? | readonly string[] | Every tag must match. |
cursor? | string | - |
limit? | number | ≤ 100, default 24. |
sort? | ProductListSort | Default name. |
Interface: ProductListResponse
Properties
| Property | Type |
|---|---|
items | ProductView[] |
nextCursor | string | null |
Interface: ProductPricing
Properties
| Property | Type | Description |
|---|---|---|
model | PricingModel | - |
amountPaise | number | null | Paise. null for free / on_request. |
maxAmountPaise | number | null | Paise, range only. |
currency | "INR" | - |
interval | | ProductPricingInterval | null | recurring only. |
unit | string | null | "hour", "nos", "kg", "visit". |
description | string | null | Free text: "per user, billed annually", "excl. installation". ≤ 280. |
mrpPaise | number | null | Strike-through price, optional. Paise. |
taxRatePercent | number | null | GST rate - 0, 5, 12, 18, 28 ...; null = not stated. |
taxInclusive | boolean | Indian retail is MRP-inclusive. |
hsnSac | string | null | HSN for goods, SAC for services. |
Interface: ProductQuoteLine
A quote/invoice line built from a catalogue entry. Tax-exclusive, as a B2B line is written.
Properties
| Property | Type | Description |
|---|---|---|
productId | string | - |
name | string | - |
description | string | null | The product's one-line summary - not its markdown description. |
unitPricePaise | number | Paise, GST-exclusive. 0 when the product states no amount - see amountMissing. |
gstRatePercent | number | null | - |
unit | string | null | - |
hsnSac | string | null | - |
amountMissing | boolean | true when the catalogue gives no number to put on the line - priced on request, or a priced model with no amount. The line carries 0 and the person building the quote has to type the price; a host should say so rather than quietly quoting nothing. A free product is a real zero and is not flagged. |
taxRateMissing | boolean | true when the price is marked tax-inclusive but states no rate, so nothing could be carved out: unitPricePaise is the inclusive figure as entered. Ask for the rate before sending the quote. |
Interface: ProductsAvailability
GET {basePath}/_status - whether a host should show the products screens at all.
Properties
| Property | Type | Description |
|---|---|---|
available | boolean | - |
reason? | "not_connected" | "unconfigured" | not_connected: the workspace has not connected Products to this application (Connect refused the delegated token). unconfigured: Connect names no Products for this application - it does not declare that it consumes products - and the host set no productsUrl override. |
Interface: ProductsErrorBody
Every error the Products API (and the relay in front of it) answers.
Properties
| Property | Type |
|---|---|
error | { code: | string & { } | ProductsErrorCode; message: string; } |
error.code | | string & { } | ProductsErrorCode |
error.message | string |
Interface: ProductTagCount
Properties
| Property | Type |
|---|---|
tag | string |
count | number |
Interface: ProductTagsResponse
Properties
| Property | Type |
|---|---|
items | ProductTagCount[] |
Interface: ProductView
Product as every read answers it: images expanded, the cover's URL, and a bundle's components resolved.
Extends
Omit<Product,"images"|"bundle">
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
id | string | - | Product.id |
kind | ProductKind | - | Product.kind |
status | ProductStatus | - | Product.status |
name | string | ≤ 160. | Product.name |
sku | string | null | ≤ 64, unique among non-archived when set. | Product.sku |
summary | string | null | One line, ≤ 280 - cards, pickers. | Product.summary |
description | string | null | Markdown, ≤ 20,000. | Product.description |
pricing | ProductPricing | - | Product.pricing |
tags | string[] | ≤ 20, trimmed, case-insensitively unique, ≤ 40 chars each. | Product.tags |
fields | Record<string, ProductFieldValue> | Custom field values by field key. | Product.fields |
externalIds | ProductExternalIds | Links to other systems' copies; set by importers. | Product.externalIds |
version | number | Optimistic concurrency - every write sends the version it read. | Product.version |
createdAt | string | - | Product.createdAt |
createdBy | string | "user:<uid>" or "app:<key>". | Product.createdBy |
updatedAt | string | - | Product.updatedAt |
updatedBy | string | - | Product.updatedBy |
archivedAt | string | null | - | Product.archivedAt |
archivedBy | string | null | - | Product.archivedBy |
images | ProductImageView[] | - | - |
coverUrl | string | null | - | - |
bundle | ProductBundleView | null | - | - |
Interface: ProxyIntegrationRequestResponse
See IntegrationsResource.request - status is the provider's, not Connect's.
Properties
| Property | Type |
|---|---|
status | number |
data | unknown |
Interface: PublishApplicationEventRequest
POST /events/publish's body.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
type | string | Must be declared in your application's exposedApi.events. |
data | Record<string, unknown> | - |
idempotencyKey | string | Unique per publisher; a repeat within 24h is accepted and not re-delivered. |
Interface: PublishApplicationEventResponse
POST /events/publish's 200.
Properties
| Property | Type | Description |
|---|---|---|
id | string | Connect's event id - the id every consumer receives. |
deduplicated | boolean | true when the idempotencyKey was already seen and nothing new was delivered. |
Interface: PushKeyResponse
Properties
| Property | Type | Description |
|---|---|---|
configured | boolean | false when the platform has no VAPID keys - say so rather than prompting. |
publicKey | string | The applicationServerKey for pushManager.subscribe(). Empty when unconfigured. |
Interface: ReadinessAnswerability
Whether an assistant could answer a buyer's question from the site's own pages.
The one judged rather than measured part of the report - a model read the
pages and decided. Everything else in checks was parsed from real responses.
Properties
| Property | Type |
|---|---|
offering | boolean |
pricing | boolean |
howToBuy | boolean |
contact | boolean |
summary | string |
gaps | string[] |
Interface: ReadinessBlocker
Properties
| Property | Type | Description |
|---|---|---|
code | | ReadinessBlockerCode | string & { } | Branch on this. Typed as a union for the codes that exist today, widened with (string & {}) because the server adds codes without a major version - an unrecognised one should be treated as blocking. |
severity | "blocking" | "advisory" | - |
message | string | Human-facing and subject to rewording. Show it, do not parse it. |
resolveUrl | string | null | Where in Connect the customer resolves this. null when nothing they do helps (e.g. a review in progress). |
graceEndsAt? | string | null | When this blocker resolves itself automatically - currently only set on payment_retry_pending, the deadline dunning suspends the subscription. undefined/null for every other code. |
requiredWorkspaceType? | "individual" | "business" | null | The account type this application requires - set only on audience_mismatch, undefined/null otherwise. Branch on this rather than reading it out of message. |
Interface: ReadinessCheck
One measured fact, with the evidence that settles it.
Properties
| Property | Type | Description |
|---|---|---|
id | string | Stable dotted id, e.g. mcp.server. Safe to key on; findings cite it. |
dimension | ReadinessDimension | - |
kind | ReadinessCheckKind | - |
title | string | - |
status | ReadinessCheckStatus | - |
weight | number | Relative importance inside its dimension. Only ratios are meaningful. |
observed | string | What was actually observed, in one line - specific enough to show the user verbatim. |
evidence | string | null | The URL that proves it, where there is one. |
Interface: ReadinessDimensionScore
Properties
| Property | Type | Description |
|---|---|---|
dimension | ReadinessDimension | - |
score | number | 0-100. |
weight | number | This dimension's share of the overall score. |
unmeasured | number | Core checks that could not be measured. Show this rather than implying full coverage. |
Interface: ReadinessFinding
One thing to do about it, anchored in the checks that motivated it.
Properties
| Property | Type | Description |
|---|---|---|
severity | "warning" | "critical" | "note" | - |
checkIds | string[] | The checks this is about. Every id here appears in checks. |
title | string | - |
detail | string | - |
fix | string | - |
effort | "medium" | "low" | "high" | - |
Interface: ReadinessReport
How ready a site is for AI agents.
The important property, and the one that makes this worth showing a customer:
the score was computed from the checks, and the checks were measured.
Whether an MCP server exists is a completed JSON-RPC handshake, not a file
that happened to answer 200; whether assistants are blocked is that site's
robots.txt evaluated by its real rules. A model contributed only
answerability and the prose in findings, and it cannot alter a status or
the score.
Properties
| Property | Type | Description |
|---|---|---|
domain | string | Normalised - lowercase, no scheme, no www.. |
score | number | 0-100, weighted across dimensions. |
grade | ReadinessGrade | - |
dimensions | ReadinessDimensionScore[] | - |
checks | ReadinessCheck[] | - |
findings | ReadinessFinding[] | Ordered by consequence, most serious first. |
answerability | ReadinessAnswerability | null | - |
topFix | | { checkId: string; title: string; fix: string; } | null | The one thing to do first. Drawn from findings, so it never disagrees with them. |
assessedAt | string | ISO-8601. Age the report against this. |
Interface: ReadinessResponse
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
applicationId | string | - |
ready | boolean | false if any blocker is "blocking". |
readiness | { source: "connect"; blockers: ReadinessBlocker[]; } | - |
readiness.source | "connect" | - |
readiness.blockers | ReadinessBlocker[] | - |
Interface: RefundCreditInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
consumeIdempotencyKey | string | The idempotencyKey the consume was sent with. |
amount | number | - |
idempotencyKey | string | - |
description | string | - |
Interface: RegisterPushDeviceInput
Exactly what PushSubscription.toJSON() produces, plus the agent string for the device list.
Properties
| Property | Type |
|---|---|
endpoint | string |
keys | { p256dh: string; auth: string; } |
keys.p256dh | string |
keys.auth | string |
userAgent | string |
Interface: RegisterPushDeviceResponse
Properties
| Property | Type | Description |
|---|---|---|
id | string | Hand this to revokeDevice to turn the same browser off again. |
Interface: ReportedUsage
One count Connect has on record for a workspace - see EntitlementsResource.reportUsage.
Properties
| Property | Type | Description |
|---|---|---|
value | number | - |
reportedAt | string | ISO 8601. When this count was last reported - shown to the customer as its age. |
Interface: ReportUsageResponse
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
counts | Record<string, ReportedUsage> | Every count on record for the workspace after this report, keyed by limit key - not only the ones just sent. |
Interface: RescheduledBooking
Properties
| Property | Type | Description |
|---|---|---|
booking | Booking | The new appointment. This is the live one. |
previous | Booking | The retired original, now rescheduled and pointing at booking. |
Interface: ResolvedPlanLimit
How much of one metered resource a plan allows, as the catalogue read
reports it: the application's own declaration (key, name, unit) joined
to this plan's answer.
The limits counterpart of PlanFeature, and it carries the same asymmetry
against SubscribedPlan.limits for the same reason - that one is a contract
keyed for lookup, this one is a row to render.
There is one entry per limit the application declares, including the ones a
tier allows none of (model: "none"). "Starter: 1 site" only means anything
beside "Growth: 5 sites", so a comparison table needs the row even where the
answer is nothing; a card showing only what a tier includes filters on
model !== "none".
Properties
| Property | Type | Description |
|---|---|---|
key | string | What getLimit and SubscribedPlan.limits are keyed by. |
name | string | Human label, e.g. "Sites". |
unit | string | The noun the number needs, e.g. "sites" - render as ${value} ${unit}. |
description | string | Empty string when the application's catalog carries no description. |
model | "unlimited" | "none" | "limited" | - |
value | number | null | Non-null iff model is "limited". |
Interface: RevokeOtherSessionsResponse
Properties
| Property | Type |
|---|---|
success | true |
revokedCount | number |
Interface: RolesResponse
Properties
| Property | Type |
|---|---|
applicationId | string |
applicationKey | string | null |
permissionCatalog | ApplicationPermission[] |
roles | ApplicationRoleDefinition[] |
Interface: ScaleStep
One step of a measured scale.
Properties
| Property | Type | Description |
|---|---|---|
value | number | - |
unit | string | px, rem, em, %. |
occurrences | number | - |
Interface: ScannedStoreDocument
One row from a cross-workspace scan - carries the workspace it belongs to.
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
id | string | - | StoreDocument.id |
data | Record<string, unknown> | The document's own fields, plus Connect's bookkeeping under _: _createdAt, _updatedAt, _createdBy, _updatedBy, and - on anything that came from a read - _updateTime. _updateTime is the value a { updateTime } precondition on commit is compared against. It is Firestore's own write timestamp rather than the _updatedAt field beside it, because a transform in the same commit can move the field and cannot touch the write timestamp. None of them may be written back - strip every _-prefixed key before sending a document you read. | StoreDocument.data |
workspaceId | string | - | - |
Interface: SendEmailInput
One email, sent on behalf of a workspace that subscribes to your
application - see EmailResource.
Hand-written here like every wire type in this file - see SendPushInput.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | The subscribing workspace this is sent on behalf of. |
to | string[] | 1-10 addresses. |
cc? | string[] | Up to 10 addresses. A copy of somebody already in to is dropped. |
subject | string | Up to 300 characters, no line breaks. |
html | string | Up to 256 KB. |
text | string | The plain-text alternative - required, up to 256 KB. |
replyTo? | string | Where the recipient's reply goes - your inbound address. |
fromName? | string | The display name, up to 80 characters. Defaults to the workspace's name. |
fromAddress? | string | The address to send from, e.g. support@acme.com. Omit it and the email leaves from Connect's own sending address, exactly as before. It must be on one of the workspace's sending domains (see connect.emailDomains) whose status lets it send - verified, or failing during its grace period (sendingDomainMaySend). Anything else is refused and nothing is sent: Connect never silently swaps the From, so the fallback is yours to choose. Needs the email:send_as scope; refusals are ConnectErrors with code from_address_not_allowed (403 - no scope, or not a domain of this workspace), sending_domain_unverified (409) or domain_warming_up (429 - a new domain's daily volume is still ramping; retry later). |
headers? | { messageId?: string; inReplyTo?: string; references?: string; } | Threading headers. Each value is <id@domain>; references is space-separated. No line breaks. |
headers.messageId? | string | A Message-ID to propose. Connect's mail provider replaces it - store the response's messageId instead. |
headers.inReplyTo? | string | - |
headers.references? | string | - |
Interface: SendEmailResponse
Properties
| Property | Type | Description |
|---|---|---|
messageId | string | The Message-ID the email left under, angle brackets included. A reply names it in In-Reply-To. |
suppressed | string[] | Recipients skipped because they hard-bounced or marked Connect's mail as spam. Empty on an ordinary send; every recipient when nothing was sent. |
from | string | The address the email actually left from - your fromAddress, or Connect's own sending address when you passed none. |
Interface: SendingDomain
A domain a workspace sends email from, owned by the workspace rather than
by any one application - see EmailDomainsResource.
verified and failing may send (sendingDomainMaySend); failing is
the grace period after a verified domain's records stop checking out, and
becomes failed after 72 hours. Timestamps are ISO-8601.
Hand-written here like every wire type in this file - see SendPushInput.
Properties
| Property | Type | Description |
|---|---|---|
domain | string | Lowercased, punycode. |
status | SendingDomainStatus | - |
mailFromDomain | string | The return-path subdomain, e.g. bounce.acme.com. |
records | SendingDomainRecord[] | - |
dmarcPolicy | "reject" | "none" | "quarantine" | null | The domain's published DMARC policy; null when it publishes none. |
ownershipVerifiedAt | string | null | - |
verifiedAt | string | null | When every required record last passed together. |
lastCheckedAt | string | null | - |
failingSince | string | null | - |
createdAt | string | - |
Interface: SendingDomainRecord
One DNS record the customer publishes for a sending domain, with the result of the last check against it.
Properties
| Property | Type | Description |
|---|---|---|
purpose | SendingDomainRecordPurpose | - |
name | string | The fully-qualified record name, e.g. _cosmoops.acme.com. |
type | "TXT" | "CNAME" | "MX" | - |
value | string | - |
priority | number | null | MX priority; null for every other type. |
required | boolean | Whether the domain can verify without it. The return-path records are recommended rather than required (DMARC passes on DKIM alone), and DMARC is asked for only when the domain publishes none. |
status | "unknown" | "ok" | "missing" | "mismatch" | unknown until the first check, or when the lookup itself failed. |
Interface: SendingDomainResponse
The 200 of add, get and check.
Properties
| Property | Type |
|---|---|
domain | SendingDomain |
Interface: SendOtpInput
Properties
| Property | Type | Description |
|---|---|---|
phoneNumber | string | E.164, e.g. +919876543210. |
channel? | OtpChannel | - |
Interface: SendOtpResponse
Properties
| Property | Type | Description |
|---|---|---|
verificationId | string | - |
status | "pending" | - |
channel | OtpChannel | - |
expiresIn | number | - |
code? | string | Test mode only. |
Interface: SendPushInput
One notification, to members of one workspace.
Hand-written here rather than imported from @connect/data, like every wire
type in this file: the published package carries none of Connect's
internals, and conformance.test.ts type-checks these against the server's
own DTOs so drift fails the build in Connect rather than in a partner's
integration.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
userIds | string[] | Connect user ids. Ids naming somebody who is not an active member are dropped rather than refused - a partner legitimately holds a stale id for somebody who has left, and failing the whole call for one of them would mean a reminder nobody gets. |
title | string | - |
body | string | - |
url | string | Absolute, and on an origin this application registered as an SSO redirect URI. |
Interface: SendPushResponse
What a fan-out actually did.
Reported honestly rather than collapsed to a boolean, because the
interesting number is usually silenced: a partner seeing zero delivered
needs to know whether nobody has a device or everybody muted them, and those
call for opposite responses.
Properties
| Property | Type | Description |
|---|---|---|
recipients | number | Members reached after preference filtering. |
silenced | number | Members dropped because they silenced notifications from their apps. |
delivered | number | Devices the notification actually reached, across every recipient. |
failed | number | - |
Interface: SendWhatsAppInput
One of your application's own Meta-approved templates, to one customer.
Hand-written here like every wire type in this file - see SendPushInput.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | The subscribing workspace this is sent on behalf of. |
template | string | The template's slug within your application, e.g. quote-issued - never its Meta name. |
language? | string | Meta language code the template was approved in. Defaults to en. |
to | string | E.164, e.g. +919876543210. |
values? | Record<string, string> | One value per template variable, by name. A missing one refuses the send. |
consent? | { collectedVia: string; } | The opt-in your application collected, recorded only when Connect has none on file for the number. A number that withdrew stays withdrawn. |
consent.collectedVia | string | - |
Interface: SendWhatsAppResponse
Properties
| Property | Type |
|---|---|
status | "sent" |
messageId | string |
Interface: SessionAccess
Who the signed-in account is to you, in three flat fields.
Replaces the applicationRole ({role, granted}) and platformAccess
({roles}) blocks, which were mutually exclusive by construction: a
Connect platform account belongs to no workspace, so one of the two was
always the empty answer and every integration wrote the same null-check
twice.
Read staff first - it decides what the other two mean.
staff is not a client-side security boundary. <ConnectPlatformGate>
and the hooks around it decide what to render; the browser can ignore all
of it. Re-check it server-side with client.identity.getUser(sessionToken)
before serving anything an operator console exposes.
Extended by
Properties
| Property | Type | Description |
|---|---|---|
role | string | null | The one role this session holds, or null. For a member (staff: false): the role matched on your application, resolved for your client_id rather than their workspace-wide roles - null when their permissions match no single role you defined, including when they have no access to your application at all. For staff (staff: true): the highest-authority Connect platform role your application accepts - never Connect's full role list for that person. An application accepting only developer is told "developer" about a platform_admin who also holds it. |
permissions | string[] | What to gate on. Permission ids from your own permissionCatalog for a member; Connect platform:* ids for staff. One vocabulary at a time, decided by staff - a member session never carries a platform:* id and a staff session never carries one of yours. |
staff | boolean | true when this is one of Connect's own operators rather than your customer. Only ever true when a Connect platform account signs in and your application has staff access turned on for that role. It is off by default; a platform admin enables it per application in the Connect Console, so until then this is always false. A staff session is shaped differently, and every difference is deliberate: user.workspaceId/workspaceName/workspaceType are null (these accounts belong to no workspace, by enforcement rather than by accident) and subscription is null (nobody is paying for staff to be here). Serve your operator console off this session and nothing else. |
Interface: SessionAudience
Whether the signed-in workspace is inside your application's audience -
returned alongside user by both exchangeCode() and getUser().
user.workspaceType answers what the account is; this answers what your
application is sold to, and whether the two agree. Both ship together so
the check stays !audience.matches rather than a hardcoded audience
constant in your own code that goes stale the moment the catalogue entry
changes.
matches: false means readiness will report an audience_mismatch blocker
and any attempt to subscribe will be refused, so treat it as a hard block:
<ConnectAccessGate> already does. What it is NOT is a reason to sign the
user out - they have a valid Connect account, just the wrong kind of one
for this application.
Properties
| Property | Type | Description |
|---|---|---|
application | ApplicationAudience | Your catalogue entry's setting - "both" if you have never narrowed it. |
requiredWorkspaceType | WorkspaceType | null | The single workspaceType application demands - null for "both", which accepts either. |
matches | boolean | false means this user is outside your audience. |
enforcedAtSignIn | boolean | Whether Connect refuses the sign-in itself on a mismatch. When true, a matches: false can only reach you on a session minted before the setting was turned on. When false, gating is entirely yours to do - which is what <ConnectAccessGate> is for. |
Interface: SessionDevice
Properties
| Property | Type |
|---|---|
userAgent | string |
browser | string | null |
browserVersion | string | null |
os | string | null |
osVersion | string | null |
deviceType | SessionDeviceType |
Interface: SessionsListResponse
Properties
| Property | Type |
|---|---|
items | ConnectUserSession[] |
Interface: SessionSubscription
The compact subscription block returned alongside identity - null when the workspace has never subscribed to this application.
Properties
| Property | Type | Description |
|---|---|---|
planId | string | - |
status | SubscriptionStatus | - |
nextBillingDate | string | null | - |
isTrialing | boolean | status alone cannot tell trialing from paying; both are "active". |
Interface: SetupProgress<K>
Type Parameters
| Type Parameter | Default type |
|---|---|
K extends string | string |
Properties
| Property | Type | Description |
|---|---|---|
steps | SetupStepStatus<K>[] | - |
done | number | - |
total | number | - |
requiredDone | number | - |
requiredTotal | number | - |
complete | boolean | Every step done. |
requiredComplete | boolean | Every required step done now, or latched by record.requiredCompletedAt. |
shouldLatch | boolean | Every required step is done now and the record is not latched yet - write requiredCompletedAt. |
next | SetupStepStatus<K> | null | Where to send the admin next - see deriveSetupProgress. |
Interface: SetupRecord
The little an application stores about its onboarding - everything else is
derived. Store it as-is (one document per workspace) and read it back
through normalizeSetupRecord.
Properties
| Property | Type | Description |
|---|---|---|
skipped | readonly string[] | Step keys the admin skipped. |
completedAt | string | null | When the admin reached the end of the wizard (ISO-8601), or null. |
requiredCompletedAt | string | null | When every required step was first done (ISO-8601) - the latch. Once set, the lock never closes again. |
Interface: SetupSignal
What the application's data says about one step - computed per request from what exists, so the checklist cannot drift from the workspace.
Properties
| Property | Type | Description |
|---|---|---|
done | boolean | - |
detail? | string | null | The fact behind the state - "2 addresses", "/portal/acme". Shown in place of the description once done. |
inReview? | string | null | Set while the step waits on a review - the reason, "Meta is reviewing your display name". |
blockedBy? | string | null | Set while something outside the step list blocks it - what it waits on, "your KYB approval". |
Interface: SetupStepDefinition<K>
One step of an application's onboarding, declared once as a constant.
Extended by
Type Parameters
| Type Parameter | Default type |
|---|---|
K extends string | string |
Properties
| Property | Type | Description |
|---|---|---|
key | K | - |
title | string | "Connect your support email" - the checklist's and the carousel's line. |
description | string | What doing it gets you, in one sentence. |
path | string | The wizard route, /onboarding/<slug>. Must start with ONBOARDING_PATH + "/". |
actionLabel | string | The verb on the button - "Add an email channel". |
required | boolean | Counts toward the lock: the application stays on /onboarding until every required step is done (once). |
permission | string | null | The permission that may act on the step, or null for anybody. A viewer without it sees the step read-only, with whose job it is (owner) - never a button that ends in a 403. |
owner | string | Who to ask, as it reads after "Ask …" - "an admin", "a workspace admin". |
after? | readonly K[] | Steps that must be done first. Until they are, this one is blocked. |
skippable? | boolean | Whether "Skip for now" is offered. Defaults to !required - a required step is skipped by being done. |
Interface: SetupStepStatus<K>
One step as drawn: its definition plus where it stands.
Extends
Type Parameters
| Type Parameter | Default type |
|---|---|
K extends string | string |
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
key | K | - | SetupStepDefinition.key |
title | string | "Connect your support email" - the checklist's and the carousel's line. | SetupStepDefinition.title |
description | string | What doing it gets you, in one sentence. | SetupStepDefinition.description |
path | string | The wizard route, /onboarding/<slug>. Must start with ONBOARDING_PATH + "/". | SetupStepDefinition.path |
actionLabel | string | The verb on the button - "Add an email channel". | SetupStepDefinition.actionLabel |
required | boolean | Counts toward the lock: the application stays on /onboarding until every required step is done (once). | SetupStepDefinition.required |
permission | string | null | The permission that may act on the step, or null for anybody. A viewer without it sees the step read-only, with whose job it is (owner) - never a button that ends in a 403. | SetupStepDefinition.permission |
owner | string | Who to ask, as it reads after "Ask …" - "an admin", "a workspace admin". | SetupStepDefinition.owner |
after? | readonly K[] | Steps that must be done first. Until they are, this one is blocked. | SetupStepDefinition.after |
skippable? | boolean | Whether "Skip for now" is offered. Defaults to !required - a required step is skipped by being done. | SetupStepDefinition.skippable |
state | SetupStepState | - | - |
detail | string | null | The line the state carries: the signal's detail (done, todo, skipped), the review reason (in_review), or what it waits on (blocked - the blockedBy, else the titles of the unfinished after steps). | - |
Interface: StartedBrowserCall
Properties
| Property | Type | Description |
|---|---|---|
callId | string | - |
toNumber | string | The number being rung, E.164. |
fromNumber | string | The workspace's own number, which the person called will see. |
headers | Record<string, string> | - |
Interface: StoreAggregatePage
Properties
| Property | Type | Description |
|---|---|---|
values | Record<string, number | null> | One number per alias. A sum or avg over nothing is null, not 0 - "nothing matched" and "the total is zero" are different facts. A count is always a number. |
Interface: StoreAggregation
One aggregation over a filtered collection.
alias is what the result is keyed by, so a single request can carry a
count and a sum without the response shape depending on argument order.
Properties
| Property | Type | Description |
|---|---|---|
op | StoreAggregationOperation | - |
field? | string | null | Required for sum/avg. count counts documents and takes none. |
alias | string | - |
Interface: StoreCollectionDeclaration
One collection as an application declares it, and the helpers that read a declaration back.
The declaration is the same shape Connect stores on Application.storeSchema
and the same one scripts/seed-*-application.ts writes there, so an app that
builds its schema from its own query matrix can hand the result straight to
anything here without a second type of its own. Connect remains the
authority - this is a local copy of what was declared, useful for deriving
things from the declaration, never for deciding access.
Isomorphic and dependency-free, like the rest of the root entry.
Properties
| Property | Type | Description |
|---|---|---|
name | string | - |
kind | "data" | "kv" | - |
scope | "application" | "workspace" | - |
description | string | Prose. Connect shows it in the Console; deriveStoreTools gives it to a model. |
readPermission | string | null | The per-user permission Connect checks when a call carries asUser. null means any member. |
writePermission | string | null | - |
crossWorkspace | boolean | Whether data.scan may read this collection across every workspace. |
appendOnly | boolean | Create-only. Connect refuses a replace, a merge or a delete for every caller. |
queryableFields | string[] | Nothing may be filtered, ordered or aggregated on a field not listed. |
indexes | { fields: StoreIndexField[]; }[] | - |
Interface: StoreCommitOperation
One write inside a commit. Each names its own collection - a commit may span several.
Properties
| Property | Type | Description |
|---|---|---|
op | StoreCommitMode | - |
collection | string | - |
id? | string | null | Omit only on create, to have an id allocated. |
data? | Record<string, unknown> | null | Absent on delete. Top-level values may be StoreFieldTransforms. |
precondition? | StorePrecondition | null | Not accepted on create, which already means "and it must not exist". |
Interface: StoreCommitResult
Properties
| Property | Type | Description |
|---|---|---|
ids | string[] | The document id each operation wrote, positionally aligned with the operations sent. |
Interface: StoreDocument
One document from the data store.
Connect's own bookkeeping rides inside data under a _ prefix -
_createdAt, _updatedAt, _createdBy, _updatedBy - rather than as
sibling fields, so a document round-trips through your code unchanged. You
cannot write a _-prefixed field; that is what makes _createdBy
trustworthy. It is "application" for a server-to-server write, or the
Connect user id when a signed-in user drove it.
Extended by
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
data | Record<string, unknown> | The document's own fields, plus Connect's bookkeeping under _: _createdAt, _updatedAt, _createdBy, _updatedBy, and - on anything that came from a read - _updateTime. _updateTime is the value a { updateTime } precondition on commit is compared against. It is Firestore's own write timestamp rather than the _updatedAt field beside it, because a transform in the same commit can move the field and cannot touch the write timestamp. None of them may be written back - strip every _-prefixed key before sending a document you read. |
Interface: StoreGetManyResult
What data.getMany answers - see POST /data-get-many.
Properties
| Property | Type | Description |
|---|---|---|
items | (StoreDocument | null)[] | Positionally aligned with the ids asked for, null where a document does not exist. A missing document is an ordinary answer here, not a 404. |
Interface: StoreIndexField
Properties
| Property | Type |
|---|---|
field | string |
direction | StoreIndexDirection |
Interface: StoreOrderBy
Properties
| Property | Type |
|---|---|
field | string |
direction | "asc" | "desc" |
Interface: StoreQueryFilter
Properties
| Property | Type | Description |
|---|---|---|
field | string | Must be one of the collection's declared queryableFields. |
op | StoreQueryOperator | - |
value | unknown | - |
Interface: StoreQueryPage
Properties
| Property | Type | Description |
|---|---|---|
items | StoreDocument[] | - |
nextCursor | string | null | Pass as the next cursor. null is the end of the collection. |
Interface: StoreScanPage
Properties
| Property | Type | Description |
|---|---|---|
items | ScannedStoreDocument[] | - |
nextCursor | string | null | {workspaceId}/{documentId}. Pass as the next cursor; null is the end. |
Interface: SubscribedPlan
The plan as attached to a live subscription - a narrower view than the catalog's Plan.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
name | string | - |
billingInterval | BillingInterval | - |
price | number | - |
currency | "INR" | - |
gstRate | number | - |
seats | PlanSeats | - |
features | string[] | Feature keys this plan unlocks. Your backend gates plan-restricted modules on this, and ConnectFeatureGate matches on it. Deliberately bare keys, unlike the catalogue's Plan.features - see PlanFeature. This is a contract, not a label: a key is stable and comparable, a display name is neither. |
limits | Record<string, PlanLimit> | How much of each metered resource this workspace may have, keyed by the limit key your application declared - see PlanLimit. Effective, not the tier's raw number: whatever the add-ons on this subscription raise a limit by is already folded in, so this is the figure to compare your own count against. The tier's base number and the add-ons that raised it are deliberately not broken out here - that is the join Connect does so nobody has to redo it per resource, per partner. Sparse, and that is the thing to get right: a key absent here is not "unlimited" and not an error, it is { model: "none" }. Read it through getLimit/planLimitFor rather than indexing the map, which is what turns a missing key into undefined and then, one ?. later, into an unbounded allowance. Same posture as features: Connect cannot intercept calls to your backend, so your backend compares its own count against this number. Connect stores the answer and hands it over. |
Interface: SubscribersResponse
Properties
| Property | Type | Description |
|---|---|---|
items | ApplicationSubscriber[] | - |
nextBefore | string | null | Pass back as before for the next page. null on the last page - which is the only reliable end-of-list signal, since a full page can still be the last one. |
Interface: SubscriptionActivatedEvent
A subscription became active for the first time: a free plan or a trial started, or a paid plan's first charge was captured. Grant what the plan includes. Not raised on a renewal, nor when a paused subscription recovers.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
subscriptionId | string | - |
applicationId | string | - |
planId | string | - |
trialEndsAt | string | null | ISO-8601 when it started on a trial, null otherwise. |
activatedAt | string | ISO-8601. |
Interface: SubscriptionCancelledEvent
A subscription ended. Fires on its own, not only as part of a deletion.
Properties
| Property | Type |
|---|---|
workspaceId | string |
subscriptionId | string |
applicationId | string |
Interface: SubscriptionDetail
Properties
| Property | Type | Description |
|---|---|---|
id | string | Deep-link target: /billing/subscriptions/{id} manages this subscription. |
status | SubscriptionStatus | - |
cancelAtPeriodEnd | boolean | - |
seatCount | number | How many workspace members currently hold a role for your application, counted when you read it. Compare it against seatCapacity. 0 on an "unlimited" plan - there is no ceiling to count against, so Connect does not pay for the count. ConnectSeatUsage renders nothing in that case for the same reason. |
seatCapacity? | number | null | How many members may hold your application's role: what a "fixed" plan includes, or the seats a "per-seat" subscription has bought (never fewer than it includes). null on "unlimited". On a per-seat plan a seat is a purchase - assigning someone past this number is refused, and the customer buys more on Connect at /billing/subscriptions/{id}?seats=N#seats. Older Connect deployments do not send it; fall back to plan.seats.included (seatCapacityOf does). |
addons | string[] | - |
currentBillingCycle | | { startDate: string; endDate: string; } | null | - |
nextBillingDate | string | null | - |
subscribedAt | string | - |
appliedDiscount | AppliedDiscount | null | null when no coupon or admin discount is in effect. |
Interface: SubscriptionResponse
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
applicationId | string | - |
hasActiveSubscription | boolean | Equivalent to subscription?.status === "active". |
subscription | SubscriptionDetail | null | - |
plan | SubscribedPlan | null | null exactly when subscription is. |
Interface: TelephonyCarrierEvent
A carrier event Connect relayed to your telephony webhook - verify it with
verifyTelephonyEvent. For answer and fallback, the XML you respond
with is executed on the call verbatim.
Properties
| Property | Type | Description |
|---|---|---|
type | "telephony.carrier_event" | - |
event | TelephonyEdgeEvent | - |
params | Record<string, string> | The carrier's form parameters, verbatim, as strings - CallUUID, From, To, RequestUUID, ... |
channel | | { id: string; workspaceId: string; address: string; } | null | The number your application handles that the event concerns, when Connect could tell. null for a browser call. |
receivedAt | string | ISO-8601. |
Interface: TelephonyDialInput
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
channelId | string | A channel your application handles in that workspace. |
to | string | E.164. |
ringTimeoutSeconds | number | 5-120. |
clientRef? | string | Your own reference for the call. |
Interface: TelephonyDialResult
Properties
| Property | Type | Description |
|---|---|---|
providerRequestId | string | The carrier's request id - arrives on the answer event as params.RequestUUID. |
Interface: TokenResponse
Who the signed-in account is to you, in three flat fields.
Replaces the applicationRole ({role, granted}) and platformAccess
({roles}) blocks, which were mutually exclusive by construction: a
Connect platform account belongs to no workspace, so one of the two was
always the empty answer and every integration wrote the same null-check
twice.
Read staff first - it decides what the other two mean.
staff is not a client-side security boundary. <ConnectPlatformGate>
and the hooks around it decide what to render; the browser can ignore all
of it. Re-check it server-side with client.identity.getUser(sessionToken)
before serving anything an operator console exposes.
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
role | string | null | The one role this session holds, or null. For a member (staff: false): the role matched on your application, resolved for your client_id rather than their workspace-wide roles - null when their permissions match no single role you defined, including when they have no access to your application at all. For staff (staff: true): the highest-authority Connect platform role your application accepts - never Connect's full role list for that person. An application accepting only developer is told "developer" about a platform_admin who also holds it. | SessionAccess.role |
permissions | string[] | What to gate on. Permission ids from your own permissionCatalog for a member; Connect platform:* ids for staff. One vocabulary at a time, decided by staff - a member session never carries a platform:* id and a staff session never carries one of yours. | SessionAccess.permissions |
staff | boolean | true when this is one of Connect's own operators rather than your customer. Only ever true when a Connect platform account signs in and your application has staff access turned on for that role. It is off by default; a platform admin enables it per application in the Connect Console, so until then this is always false. A staff session is shaped differently, and every difference is deliberate: user.workspaceId/workspaceName/workspaceType are null (these accounts belong to no workspace, by enforcement rather than by accident) and subscription is null (nobody is paying for staff to be here). Serve your operator console off this session and nothing else. | SessionAccess.staff |
idToken | string | - | - |
tokenType | "Bearer" | - | - |
expiresIn | number | Seconds until idToken expires - about 5 minutes. | - |
sessionToken | string | Opaque, revocable. Store this; it is the only part of the response meant to outlive the callback. | - |
sessionTokenExpiresIn | number | Seconds until sessionToken expires - about 15 days. | - |
user | ConnectUser | - | - |
subscription | SessionSubscription | null | - | - |
audience | SessionAudience | Whether this account is the kind your application serves - see SessionAudience. | - |
Interface: TypefaceEntry
Properties
| Property | Type | Description |
|---|---|---|
family | string | The first named family in the stack - the one the company chose, not the fallbacks. |
role | TypefaceRole | null | - |
weights | number[] | - |
webfontUrl | string | null | - |
occurrences | number | - |
Interface: UploadedFile
Properties
| Property | Type |
|---|---|
id | string |
fileName | string |
mimeType | string |
size | number |
url | string |
Interface: UserResponse
Who the signed-in account is to you, in three flat fields.
Replaces the applicationRole ({role, granted}) and platformAccess
({roles}) blocks, which were mutually exclusive by construction: a
Connect platform account belongs to no workspace, so one of the two was
always the empty answer and every integration wrote the same null-check
twice.
Read staff first - it decides what the other two mean.
staff is not a client-side security boundary. <ConnectPlatformGate>
and the hooks around it decide what to render; the browser can ignore all
of it. Re-check it server-side with client.identity.getUser(sessionToken)
before serving anything an operator console exposes.
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
role | string | null | The one role this session holds, or null. For a member (staff: false): the role matched on your application, resolved for your client_id rather than their workspace-wide roles - null when their permissions match no single role you defined, including when they have no access to your application at all. For staff (staff: true): the highest-authority Connect platform role your application accepts - never Connect's full role list for that person. An application accepting only developer is told "developer" about a platform_admin who also holds it. | SessionAccess.role |
permissions | string[] | What to gate on. Permission ids from your own permissionCatalog for a member; Connect platform:* ids for staff. One vocabulary at a time, decided by staff - a member session never carries a platform:* id and a staff session never carries one of yours. | SessionAccess.permissions |
staff | boolean | true when this is one of Connect's own operators rather than your customer. Only ever true when a Connect platform account signs in and your application has staff access turned on for that role. It is off by default; a platform admin enables it per application in the Connect Console, so until then this is always false. A staff session is shaped differently, and every difference is deliberate: user.workspaceId/workspaceName/workspaceType are null (these accounts belong to no workspace, by enforcement rather than by accident) and subscription is null (nobody is paying for staff to be here). Serve your operator console off this session and nothing else. | SessionAccess.staff |
user | ConnectUser | - | - |
subscription | SessionSubscription | null | - | - |
audience | SessionAudience | Live, like subscription and role/permissions/staff - a workspace that converted to a business since sign-in matches here without another sign-in. | - |
sessionExpiresAt | string | ISO 8601. Refresh proactively off this rather than waiting for a 401. | - |
Interface: UserRolesResponse
Properties
| Property | Type |
|---|---|
userId | string |
workspaceId | string |
role | string | null |
granted | string[] |
Interface: UserWorkspace
One workspace the signed-in user belongs to - a row in a workspace switcher.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
name | string | - |
type | WorkspaceType | - |
isActive | boolean | The workspace this session is currently pinned to. |
inAudience | boolean | Whether your application is sold to this kind of workspace. false rows are worth showing disabled with the reason - a workspace that silently disappears from a switcher reads as data loss to the person who owns it. |
subscribed | boolean | Whether it holds an entitling subscription to your application. |
Interface: VerifyOtpInput
Properties
| Property | Type | Description |
|---|---|---|
verificationId | string | - |
code | string | 6 digits. |
Interface: VerifyOtpResponse
Properties
| Property | Type |
|---|---|
verificationId | string |
status | "pending" | "approved" | "expired" | "max_attempts_exceeded" |
phoneNumber | string |
Interface: VisitorAgentMessage
A person's reply, as visitor.sessions.appendAgentMessage stored it.
Extends
Properties
| Property | Type | Description | Overrides | Inherited from |
|---|---|---|---|---|
text | string | - | - | VisitorMessage.text |
at | string | - | - | VisitorMessage.at |
agentName | string | null | Who wrote an agent message, as you named them. null on every other role. | - | VisitorMessage.agentName |
role | "agent" | - | VisitorMessage.role | - |
Interface: VisitorAppointment
The appointment visitor.book placed, and the one a reopened conversation
reports.
VisitorBooking plus label, and the extra field is the point: the words are
produced by the same formatter the confirmation email uses, so the chat and
the email cannot read differently about the same appointment. Render it rather
than formatting startAt yourself - the visitor's browser is in their zone and
the appointment is in the host's, and Intl's month names differ between
engines besides.
Extends
Properties
| Property | Type | Description | Inherited from |
|---|---|---|---|
startAt | string | - | VisitorBooking.startAt |
endAt | string | - | VisitorBooking.endAt |
timezone | string | The workspace's zone, not the visitor's - the zone the confirmation uses. | VisitorBooking.timezone |
label | string | - | - |
Interface: VisitorBooking
An appointment this conversation booked, as a visitor is allowed to see it.
Deliberately not the booking: no host ids, no workspace ids, no internal reference - the same narrowing Connect's own public booking page makes.
Extended by
Properties
| Property | Type | Description |
|---|---|---|
startAt | string | - |
endAt | string | - |
timezone | string | The workspace's zone, not the visitor's - the zone the confirmation uses. |
Interface: VisitorHandoff
A person holds the conversation, so the assistant answers none of it.
Set and cleared with visitor.sessions.handoff. While it is set your widget
should say who it is talking to ("You're talking to Acme support", from
label) and poll visitor.startSession({ resume, since }) for the person's
replies.
Properties
| Property | Type | Description |
|---|---|---|
state | "human" | - |
label | string | null | - |
at | string | ISO-8601 - when the hand-off was set. |
Interface: VisitorKnowledgeItem
One thing a workspace's website chat is allowed to tell strangers.
Not the workspace's Context Index. That corpus is fed by every Drive upload; this store holds only what somebody explicitly published, which is what makes it safe to answer an anonymous visitor from. Nothing arrives here as a side effect of anything else.
Properties
| Property | Type | Description |
|---|---|---|
id | string | - |
title | string | - |
body | string | - |
source | VisitorKnowledgeSource | - |
sourceRef | string | null | The crawled URL, or the uploaded file's name. null for authored text. |
status | "draft" | "published" | - |
indexState | VisitorKnowledgeIndexState | - |
indexError | string | null | Why the last index attempt failed, for your own screen to show. |
publishedAt | string | null | ISO-8601, or null for a draft. |
createdAt | string | - |
updatedAt | string | - |
Interface: VisitorKnowledgeList
The knowledge base, and how its published half reaches the model.
mode is a property of the set, which is why it is here and not on each
item - and why a screen should read it from here rather than inferring it.
Properties
| Property | Type | Description |
|---|---|---|
items | VisitorKnowledgeItem[] | Drafts included: this is the list a customer audits. |
mode | "none" | "inline" | "retrieval" | - |
Interface: VisitorMessage
One stored message in a visitor conversation. at is ISO-8601.
Extended by
Properties
| Property | Type | Description |
|---|---|---|
role | VisitorMessageRole | - |
text | string | - |
at | string | - |
agentName | string | null | Who wrote an agent message, as you named them. null on every other role. |
Interface: VisitorSession
A conversation, as visitor.startSession opens or reopens it.
It carries no greeting and no suggestions. Those are your own widget settings, rendered from your own store - so your bubble paints its first frame with no call to Connect at all, and this exists only to mint or recover the conversation Connect holds.
Properties
| Property | Type | Description |
|---|---|---|
sessionId | string | Hold this and send it with every turn. Treat it as a credential: it is the only thing identifying the conversation, and anyone holding it can continue it. Keep it in sessionStorage - it should survive a reload, which is what lets somebody follow a link out of the chat and come back, and it should not travel to another tab or outlive the visit. |
messages | VisitorMessage[] | The transcript, oldest first. Empty on a new conversation, and only the messages newer than since when you passed one. |
handoff | VisitorHandoff | null | Set while a person holds the conversation - see VisitorHandoff. |
field | VisitorField | - |
turnsRemaining | number | - |
booking | VisitorAppointment | null | - |
Interface: VisitorSlot
One time the assistant may offer, as its slot lookup returned it.
Properties
| Property | Type | Description |
|---|---|---|
startAt | string | ISO-8601. Pass this back verbatim to book it. |
label | string | Already written out in the workspace's own timezone - render this, don't reformat. |
Interface: VisitorSlotOffer
The times a turn looked up, lifted out of the tool result.
Draw the buttons from this, never from the assistant's sentence. A time the
visitor taps is then one the server offered, which is what makes
visitor.book unable to take a time nobody was given.
Properties
| Property | Type |
|---|---|
eventType | string |
durationMinutes | number |
timezone | string |
slots | VisitorSlot[] |
Interface: VisitorToolCall
What a tool did this turn. result is the tool's own payload - switch on name.
Properties
| Property | Type |
|---|---|
name | string |
result | unknown |
Interface: VisitorTurnDone
The done event at the end of a streamed turn.
Properties
| Property | Type | Description |
|---|---|---|
toolCalls | VisitorToolCall[] | - |
field | VisitorField | - |
turnsRemaining | number | - |
grounded | boolean | Whether the reply was grounded in published knowledge. false means nothing published covered the question, so the assistant said so and offered a call. Worth recording: a run of them is a knowledge base with a gap in it, which is the most useful signal this endpoint produces. |
handoff | boolean | A person holds the conversation: the message was stored and nothing answered it - no delta came before this, and no turn was spent. Show the message as sent and wait for the person's reply on your poll. |
Interface: WeeklyHours
A week of working hours, one entry per weekday - 0 is Sunday. An empty
array means closed that day; every key is required, so "closed on Sunday"
is an array the caller had to mean rather than a key they forgot.
Properties
| Property | Type |
|---|---|
0 | WeeklyInterval[] |
1 | WeeklyInterval[] |
2 | WeeklyInterval[] |
3 | WeeklyInterval[] |
4 | WeeklyInterval[] |
5 | WeeklyInterval[] |
6 | WeeklyInterval[] |
Interface: WeeklyInterval
One interval within a day, in minutes since midnight - 540 is 9am.
Properties
| Property | Type |
|---|---|
startMinute | number |
endMinute | number |
Interface: WhatsAppTemplateState
Whether one of your templates would send right now. Branch on live.
Properties
| Property | Type | Description |
|---|---|---|
slug | string | - |
language | string | - |
name | string | Meta's name for it: app_<applicationKey>_<slug>. |
status | WhatsAppTemplateReviewStatus | - |
live | boolean | Approved by Meta and currently served. |
variables | string[] | The values a send must carry, from the approved content. |
Interface: WorkspaceApp
Properties
| Property | Type | Description |
|---|---|---|
applicationId | string | - |
applicationKey | string | null | Stable, human-readable key - branch on this, not applicationId, which is per-environment. |
name | string | - |
description | string | Plain text, HTML already stripped server-side. |
iconUrl | string | null | Uploaded brand icon. null falls back to icon. |
icon | string | A lucide-react icon name from Connect's curated set. |
category | WorkspaceAppCategory | Catalog classification, if you want to group or filter the grid by it. |
launchUrl | string | null | Where a tile navigates. null when the application registered no site. |
subscribed | boolean | Whether this workspace holds a launchable subscription. /apps answers with the platform's whole active catalog, not only what the workspace bought, so this is the split a launcher renders as two groups: the apps you open, and the apps you could. false means the tile belongs in the second - send it to Connect, not to launchUrl. |
isCurrent | boolean | True for the application whose API key made the call - render it as the active tile. |
needsAttention | boolean | Launchable but not healthy (paused, or cancelling at period end) - badge it, do not hide it. Always false when subscribed is false. |
Interface: WorkspaceBillingAddress
A workspace's registered address, as its billing profile reports it.
stateCode is the Indian state code, and it is the field that matters most
of the six: it decides CGST+SGST versus IGST on anything you raise against
this entity.
Properties
| Property | Type |
|---|---|
line1 | string | null |
line2 | string | null |
city | string | null |
stateCode | string | null |
postalCode | string | null |
countryCode | string |
Interface: WorkspaceBillingProfile
A workspace's self-declared billing identity - what the customer gave CosmoOps so we can invoice them.
Read it to prefill, not to share
This is the Bill To side: who we invoice for the subscription. Your own application's letterhead is the Bill From side. For a single-entity customer they are the same three facts, which is the whole reason this is readable - a GSTIN typed twice is a GSTIN that diverges, and the copy on a quotation is the one that decides how the tax splits.
They are not the same field, though. A group that bills us through a holding company while quoting under a trading subsidiary has to stay expressible, so copy this into your own settings on first setup and keep your copy authoritative. There is deliberately no write form: an application must not be able to edit the details its own invoices are raised against.
Nothing here is verified
It is what the customer typed, not a KYB record - no PAN check stands behind it. A convenience, never evidence of identity.
Every field is nullable, because every one is optional to a customer who has not needed an invoice yet. Fall back to the workspace name you already hold on the session rather than printing an empty letterhead.
Properties
| Property | Type | Description |
|---|---|---|
legalName | string | null | The registered entity name, or null - most workspaces never set one. |
taxId | string | null | GSTIN, 15 characters. null for an unregistered customer. |
billingAddress | WorkspaceBillingAddress | null | null as a whole when none was given - one check, not six. |
businessType | string | null | The legal entity form, self-declared. null for an individual workspace. |
Interface: WorkspaceDeletedEvent
The grace window closed and Connect purged the workspace. Purge your own copy.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
purgedAt | string | ISO-8601. |
policy | ConnectDeletionPolicyNotice | null | The same frozen policy workspace.deletion_scheduled carried, repeated here because this is the event you act on: whether a class is destroyed now or moved to a retained store for another retainDays is this object's answer, not your code's. |
Interface: WorkspaceDeletionCancelledEvent
The deletion was withdrawn and the workspace is live again.
Handle this one. It is the event integrations forget, and its absence
is the worst failure of the set: an application that froze a workspace on
workspace.deletion_scheduled and never unfreezes leaves a paying
customer locked out of something that has no idea anything changed.
Properties
| Property | Type |
|---|---|
workspaceId | string |
Interface: WorkspaceDeletionScheduledEvent
A workspace's deletion has been confirmed and the grace window is running.
Access is already denied - readiness reports
workspace_pending_deletion from this moment - so treat this as "stop
writing", not "start planning". purgeAfter is what makes this event worth
having: the customer's data still exists until then, so an export or a
wind-down can run on the same clock rather than after everything is gone.
Properties
| Property | Type | Description |
|---|---|---|
workspaceId | string | - |
purgeAfter | string | ISO-8601. When Connect destroys the workspace. |
requestedBy | "owner" | "platform_admin" | - |
graceDays | number | - |
policy | ConnectDeletionPolicyNotice | null | Your declared policy for this deletion - including how long each data class may outlive the purge. null for a deletion confirmed before policies existed, which is the only case where you fall back to your own records. |
Interface: WorkspaceSwitchResult
The result of a workspace switch.
sessionToken is a new credential - the one you switched from is
revoked. Persist this before navigating, or the user is signed out.
Properties
| Property | Type |
|---|---|
sessionToken | string |
sessionTokenExpiresIn | number |
workspaceId | string |
workspaceName | string |
workspaceType | WorkspaceType |
Interface: WriteLogInput
Properties
| Property | Type | Description |
|---|---|---|
level? | LogLevel | - |
message | string | - |
metadata? | Record<string, unknown> | - |
timestamp? | string | Date | ISO 8601 or a Date. Defaults to now. |
sessionToken? | string | Attribute this event to an end user, by their opaque Connect sessionToken - the same one the token exchange handed you. There is no userId to send, deliberately. The token is verified and checked to have been minted for your application before anything is attributed, exactly as applicationId comes from your API key rather than your body. An actor a caller could type is not an actor worth recording. Omit it for genuine server-to-server events with no user behind them. |
Interface: WriteLogResponse
Properties
| Property | Type | Description |
|---|---|---|
delivered | boolean | false when the write failed - see LogsResource.write, which never throws. |
attributed | boolean | Whether a supplied sessionToken resolved to a user. false with delivered: true means the line was stored unattributed - an expired or revoked token. The event is deliberately kept rather than rejected: losing telemetry because attribution failed is the worse of the two outcomes. Always false when no sessionToken was sent. |
Type Aliases
Type Alias: AiFinishReason
type AiFinishReason = "stop" | "length" | "tool-calls" | "content-filter" | "error" | "other";Why the model stopped - the AI SDK's own vocabulary.
Type Alias: AiGenerateMessage
type AiGenerateMessage =
| {
role: "user";
content: string;
}
| {
role: "assistant";
content: string;
toolCalls?: AiGenerateToolCall[];
}
| {
role: "tool";
toolCallId: string;
toolName: string;
output: unknown;
};One message of the conversation you send. To continue after a tool call,
send the model's turn back as an assistant message carrying its
toolCalls, then one tool message per call with what running it produced.
Type Alias: AiMetering
type AiMetering = "tokens" | "application";How a token-metered AI call counts against the workspace's AI credits.
"tokens"(the default) - Connect converts the call's tokens to credits."application"- your application prices this work itself throughai.spendCredits, so the call's tokens are recorded (guardrail and attribution) but add no credits. Pass it on every call a spend covers, or the customer pays twice for one answer.
Type Alias: AiStreamInput
type AiStreamInput = Omit<AiGenerateInput, "responseSchema">;What ai.stream takes: generate's input without responseSchema, which a
stream refuses - a structured answer only exists once it has fully parsed.
Type Alias: AiTone
type AiTone = "neutral" | "positive" | "warning" | "danger";Type Alias: AiToolName
type AiToolName =
| "showTable"
| "showChart"
| "showMetrics"
| "showTimeline"
| "showDetails"
| "showSteps"
| "showSources"
| "showActions";The tools this client knows how to draw. Anything else in the stream is ignored.
Type Alias: AiValue
type AiValue = string | number | null;One cell. null is "no value" and renders as an em dash.
Type Alias: AiValueFormat
type AiValueFormat =
| "text"
| "number"
| "currency"
| "percent"
| "date"
| "datetime"
| "duration"
| "bytes"
| "status";How a scalar should be presented. See AiValue for what each one expects.
Type Alias: AnalyticsAggregation
type AnalyticsAggregation = "count" | "sum" | "distinct" | "gauge";How a declared metric turns its event into a number.
count- occurrences of the event.sum- the sum of the events'value.distinct- distinct users who produced it. Never available for today.gauge- a level: the lastvaluein the bucket, never summed across days.
Type Alias: AnalyticsGranularity
type AnalyticsGranularity = "day" | "week" | "month" | "total";How a series is bucketed. Buckets are local days in the workspace's own time zone.
Type Alias: AnalyticsPeriodPreset
type AnalyticsPeriodPreset = typeof ANALYTICS_PERIOD_PRESETS[number];Type Alias: ApplicationAudience
type ApplicationAudience = "b2b" | "b2c" | "both";Type Alias: AssessmentAnswers
type AssessmentAnswers = Record<string, string[]>;Answers, keyed by section key.
Type Alias: AssessmentFindingSeverity
type AssessmentFindingSeverity = "critical" | "warning" | "note";Type Alias: BillingInterval
type BillingInterval = "monthly" | "annual" | "one-time";How often a plan bills.
"one-time" is a plan that is paid for once and does not renew, so it has
no cadence to pair with: planGroup still groups it, but there is no
monthly/annual toggle to render for it. A pricing grid that assumes two
cadences will show it under whichever tab happens to be selected - check the
interval before offering the switch.
Type Alias: BookingSource
type BookingSource = "agent-call" | "portal" | "api";Where a booking came from - usually the first question anyone asks about an appointment they did not expect.
api is your integration; portal is the workspace's own screen or its
public booking link; agent-call is a voice agent, mid-call.
Type Alias: BookingStatus
type BookingStatus = "hold" | "confirmed" | "cancelled" | "rescheduled" | "no_show";Type Alias: BrandCitationSource
type BrandCitationSource = "website" | "stylesheet" | "render";Where a brand claim was read. Its own union rather than EnrichmentSource,
which has no notion of a stylesheet or a rendered page.
Type Alias: BrandExtractionResult
type BrandExtractionResult =
| {
status: "extracted";
profile: BrandProfile;
cached: boolean;
}
| {
status: "skipped";
reason: BrandSkipReason;
};Union Members
Type Literal
{
status: "extracted";
profile: BrandProfile;
cached: boolean;
}status
status: "extracted";profile
profile: BrandProfile;cached
cached: boolean;Whether Connect served this from its platform-wide cache. The profile is the same either way.
Type Literal
{
status: "skipped";
reason: BrandSkipReason;
}Type Alias: BrandSkipReason
type BrandSkipReason =
| "no_domain"
| "consumer_domain"
| "blocked_domain"
| "unreachable"
| "no_signal"
| "no_stylesheet";Why there is nothing to report. A normal 200, not an error.
consumer_domain and blocked_domain will never succeed on retry.
unreachable may. no_stylesheet means the site answered but shipped no
CSS Connect could measure - a real outcome for a page built entirely in
inline styles.
Type Alias: BusinessModel
type BusinessModel = "product" | "service" | "hybrid";Whether the company sells a thing, an engagement, or both.
Type Alias: CallDirection
type CallDirection = "inbound" | "outbound";Which way the call went.
Type Alias: CallDisconnectReason
type CallDisconnectReason =
| "caller_hangup"
| "agent_hangup"
| "max_duration"
| "transferred"
| "error"
| "abandoned";How a call that actually ran came to an end. Widened at its use site for the same reason as CallRejectReason.
Type Alias: CallHandler
type CallHandler = "agent" | "member";Who was on Connect's end of a call: one of the workspace's agents, or a member calling from a browser.
Type Alias: CallingAvailabilityStatus
type CallingAvailabilityStatus =
| "available"
| "no_permission"
| "no_number"
| "number_pending"
| "no_balance";Why a member can or cannot call - see calls.getCallingAvailability.
no_permission- they lack "Make calls". Don't offer calling.no_number- the workspace has no business number.offersays where to buy one (a "Business number" add-on on one of its subscriptions).number_pending- one was bought but Connect's pool was empty; an admin can retry from Connect's Calling page.no_balance- a number, but not a minute's worth of prepaid minutes.available.
Type Alias: CallRecordingStatus
type CallRecordingStatus = "none" | "pending" | "stored" | "failed";Where the recording has got to. pending is normal for a call that has just
ended - the carrier posts the audio afterwards - and failed is kept
distinct from none so "we never recorded this" is never read as "we
recorded it and lost it".
Type Alias: CallRejectReason
type CallRejectReason =
| "unknown_number"
| "channel_suspended"
| "channel_released"
| "channel_unassigned"
| "no_routing"
| "workspace_blocked"
| "answer_unavailable"
| "runtime_unconfigured";Why a call was refused. Widened at the point of use with (string & {}),
the same way ReadinessBlocker.code is: Connect adds reasons as it learns
to tell failures apart, and a new one should reach you as a value to handle
rather than as a type error on a published package you cannot patch. Handle
the ones you branch on and treat the rest as "refused".
Type Alias: CallSentiment
type CallSentiment = "positive" | "neutral" | "negative";How the person called came across, by the end of the call.
Type Alias: CallStatus
type CallStatus = "dialing" | "answered" | "rejected" | "unanswered" | "completed";rejected means Connect never connected it - see rejectReason.
answered is a call still in progress; completed is one that ran and
ended.
dialing and unanswered are an outbound call's: ringing, and nobody
picked up - see unansweredReason. A call your <ConnectCallButton>
placed passes through dialing on its way to one of the other two.
Type Alias: CallTurnRole
type CallTurnRole = "user" | "agent";Who spoke.
Type Alias: CallUnansweredReason
type CallUnansweredReason = "no_answer" | "busy" | "failed" | "cancelled";Why an outbound call reached nobody. Widened at its use site like CallRejectReason.
Type Alias: ChannelKind
type ChannelKind = "voice" | "meeting" | "whatsapp";What kind of conversation a channel carries. voice is the only kind
Connect stocks inventory for today, so claim refuses the other two:
meeting has no runtime yet, and customer whatsapp is provided by the
CosmoOps Messaging application rather than by Connect.
Type Alias: ChannelStatus
type ChannelStatus = "active" | "suspended" | "released";suspended and released both stop the channel answering. The difference
is whose act it was: a Connect operator suspended it, or the number went
back to inventory.
Type Alias: CompanyEnrichmentResult
type CompanyEnrichmentResult =
| {
status: "enriched";
profile: EnrichedCompany;
cached: boolean;
}
| {
status: "skipped";
reason: EnrichmentSkipReason;
};Union Members
Type Literal
{
status: "enriched";
profile: EnrichedCompany;
cached: boolean;
}status
status: "enriched";profile
profile: EnrichedCompany;cached
cached: boolean;Whether Connect served this from its platform-wide cache. The profile is the same either way.
Type Literal
{
status: "skipped";
reason: EnrichmentSkipReason;
}Type Alias: CompanySizeBand
type CompanySizeBand = "1-10" | "11-50" | "51-200" | "201-500" | "500+";Coarse on purpose - a headcount read off a homepage is a claim, not a measurement.
Type Alias: ConnectAiStreamEvent
type ConnectAiStreamEvent =
| {
type: "text-delta";
delta: string;
}
| {
type: "reasoning-delta";
delta: string;
}
| {
type: "tool-call";
toolCallId: string;
toolName: string;
input: unknown;
providerMetadata: Record<string, unknown> | null;
}
| {
type: "finish";
finishReason: AiFinishReason;
usage: {
inputTokens: number;
outputTokens: number;
};
};One event of ai.stream (POST /api/v1/application/ai/stream).
text-delta/reasoning-delta- the answer, and the model's thought summary when it offers one. Reasoning is informational; never send it back.tool-call- always complete, never partial input. Run it and continue exactly as withgenerate, carryingproviderMetadataback on theassistantmessage unchanged.finish- exactly one, last, with the tokens counted against the workspace's monthly budget.
A failure after the stream started is an error event on the wire, which
ai.stream throws as a ConnectError carrying the server's code rather
than yielding - so a loop over it either reaches finish or throws.
Union Members
Type Literal
{
type: "text-delta";
delta: string;
}Type Literal
{
type: "reasoning-delta";
delta: string;
}Type Literal
{
type: "tool-call";
toolCallId: string;
toolName: string;
input: unknown;
providerMetadata: Record<string, unknown> | null;
}type
type: "tool-call";toolCallId
toolCallId: string;toolName
toolName: string;input
input: unknown;providerMetadata
providerMetadata: Record<string, unknown> | null;Opaque (Gemini 3's thought signature). null when the provider attached none. Carry it back verbatim.
Type Literal
{
type: "finish";
finishReason: AiFinishReason;
usage: {
inputTokens: number;
outputTokens: number;
};
}Type Alias: ConnectCallDirection
type ConnectCallDirection = "inbound" | "outbound";Which way a call went.
Type Alias: ConnectCallPurpose
type ConnectCallPurpose = "service" | "transactional" | "promotional";What a call is for, declared by the caller - Connect places it from a
workspace number allowed to carry it. In India the series decides: regular
numbers carry service and transactional, 140-series numbers carry
promotional only. Omit it and the call is placed as before and recorded
as undeclared.
Type Alias: ConnectContactSource
type ConnectContactSource = "call" | "manual" | "import" | "api" | "extraction";How Connect came to know a contact.
extraction is the interesting one: an agent's skill captured them from
what was said, with nobody typing anything. call means the number was seen
on a call. manual and import are the two a human stands behind, and
Connect lets only those overwrite a field that already has a value.
Type Alias: ConnectDataDisposition
type ConnectDataDisposition = "erase" | "anonymize" | "retain";What becomes of one class of data when the workspace is purged.
Type Alias: ConnectDoNotCallReason
type ConnectDoNotCallReason =
| "asked_on_call"
| "asked_in_writing"
| "complaint"
| "imported"
| "other";Why a person asked not to be called - the CRM's own vocabulary.
Type Alias: ConnectFiduciaryRole
type ConnectFiduciaryRole = "connect_owned" | "processor" | "independent";Who answers for your copy of the customer's data. See the deletion-policy guide.
Type Alias: ConnectMetric
type ConnectMetric =
| "usage.events"
| "usage.active_users"
| "api.requests"
| "api.errors"
| "billing.mrr_paise"
| "billing.active_subscriptions"
| "metering.addon_consumed";The metrics Connect can report for your application.
Hand-written here rather than imported from Connect's own registry, like
every other wire type in this file, so the published package carries none of
Connect's internals. conformance.test.ts type-checks these against the
server DTOs, so drift fails the build in Connect's repo rather than in a
partner's integration.
Type Alias: ConnectTokenErrorCode
type ConnectTokenErrorCode =
| "invalid_request"
| "invalid_client"
| "invalid_grant"
| "invalid_scope"
| "invalid_target"
| "unauthorized_client"
| "access_denied";RFC 6749 §5.2 error codes the token endpoint answers with. A refusal
arrives as a ConnectError whose code is one of these.
Type Alias: ConnectWebhookEventType
type ConnectWebhookEventType =
| "workspace.deletion_scheduled"
| "workspace.deletion_cancelled"
| "workspace.deleted"
| "subscription.cancelled"
| "subscription.activated"
| "call.completed"
| "call.missed"
| "call.analyzed"
| "booking.confirmed"
| "booking.cancelled"
| "booking.rescheduled"
| "contact.created"
| "contact.updated"
| "application.event";Every event Connect will POST to your webhook endpoint.
Additive only - Connect adds event types, never renames one - so a
switch that does not handle a future type is safe, and
createConnectWebhookHandler simply ignores anything you have not
registered a handler for.
Type Alias: ContactKey
type ContactKey =
| {
phone: string;
email?: string | null;
}
| {
phone?: string | null;
email: string;
};The key a contact write is found or created by: a phone, an email, or both - never neither. The phone is matched first, then the email.
Union Members
Type Literal
{
phone: string;
email?: string | null;
}phone
phone: string;E.164. Matched first.
email?
optional email?: string | null;Matched when the phone finds nobody. Lowercased and trimmed, nothing folded.
Type Literal
{
phone?: string | null;
email: string;
}Type Alias: ContactSource
type ContactSource = "call" | "manual" | "import" | "api" | "extraction";How a contact came to exist. call means a voice agent created it from an
inbound call; manual and import are the two a human stands behind.
Type Alias: ContactUpsertMatch
type ContactUpsertMatch = "phone" | "email" | "created";Which key found the contact - created when neither did.
Type Alias: ContactUpsertResult
type ContactUpsertResult = ContactInteractionResult;contacts.upsert answers the same shape.
Type Alias: CreditEntryType
type CreditEntryType = "topup" | "consume" | "refund" | "adjustment" | "migration";Type Alias: CssFramework
type CssFramework = "tailwind" | "bootstrap" | "material" | "chakra" | "bulma";A CSS framework detected by signature.
Worth checking first, because it tells you how to read everything else: a
Tailwind site's spacing scale is Tailwind's defaults rather than a
considered choice, and a palette full of --bs-* is Bootstrap's.
Type Alias: EnrichmentSkipReason
type EnrichmentSkipReason =
| "no_domain"
| "consumer_domain"
| "blocked_domain"
| "unreachable"
| "no_signal";Why there is nothing to report. A normal outcome, not an error - most records legitimately have nothing to enrich.
consumer_domain and blocked_domain will never succeed on retry.
unreachable and no_signal may, later.
Type Alias: EnrichmentSource
type EnrichmentSource = "domain" | "registry" | "website";Where a value came from.
domain- the input itself.urlisnull, because there is no page.website- a page on the company's own site, aturl.registry- a statutory register. Reserved; nothing returns it yet.
Type Alias: IntegrationConnectionStatus
type IntegrationConnectionStatus =
| "not_connected"
| "pending"
| "active"
| "failed"
| "disconnected"
| "expired";not_connected never travels over the wire as a persisted state - it's
what ListWorkspaceIntegrationsUseCase reports for a toolkit this
workspace has no IntegrationConnection record for at all.
Type Alias: IntegrationProxyMethod
type IntegrationProxyMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";Type Alias: LogLevel
type LogLevel = "debug" | "info" | "warn" | "error";Type Alias: LogoKind
type LogoKind = "primary" | "mark" | "wordmark" | "favicon" | "social";Type Alias: LogoutScope
type LogoutScope = "device" | "all";Type Alias: OfficialNameSource
type OfficialNameSource = "registry" | "website-legal";Where an official name came from.
website-legal is a legal disclosure the company published itself - a
footer copyright line, or the entity named on its Terms page - matched by
code against a corporate suffix and cited. registry is a statutory
register and outranks it. Neither is a model's recall.
Type Alias: OtpChannel
type OtpChannel = "whatsapp" | "sms";Type Alias: PaletteRole
type PaletteRole =
| "primary"
| "secondary"
| "accent"
| "background"
| "surface"
| "text"
| "muted";Type Alias: PaletteSource
type PaletteSource = "css-var" | "computed" | "frequency";How a colour was obtained, which is how much it is worth.
css-var is the company naming its own colour in its own tokens - the
strongest signal short of a brand manual. computed comes from a rendered
page. frequency is a ranked count of declarations and is the weakest.
Type Alias: PresenceBand
type PresenceBand = "absent" | "emerging" | "established" | "strong";Where a score sits, coarsely. Advisory - use it to pick wording, not to make a decision.
Type Alias: PresenceCategory
type PresenceCategory = "findability" | "identity" | "social" | "contactability" | "trust";The five things a presence is scored on.
Type Alias: PresenceCriterionStatus
type PresenceCriterionStatus = "pass" | "partial" | "fail";How a check came out.
partial is a real third state rather than a rounded failure: a site with
og:title but no og:image has done most of the work, and rendering that as
a failure is both wrong and discouraging.
Type Alias: PresenceNetwork
type PresenceNetwork =
| "linkedin"
| "instagram"
| "facebook"
| "youtube"
| "google_business"
| "crunchbase"
| "x"
| "tiktok"
| "github"
| "other";The networks a scan recognises. other is a profile that matched no known pattern, kept rather than dropped.
Type Alias: PresenceScanResult
type PresenceScanResult =
| {
status: "scanned";
scan: PresenceScan;
cached: boolean;
}
| {
status: "skipped";
reason: PresenceSkipReason;
};Union Members
Type Literal
{
status: "scanned";
scan: PresenceScan;
cached: boolean;
}status
status: "scanned";scan
scan: PresenceScan;cached
cached: boolean;Whether Connect served this from its platform-wide cache. The scan is the same either way.
Type Literal
{
status: "skipped";
reason: PresenceSkipReason;
}Type Alias: PresenceSkipReason
type PresenceSkipReason =
| "no_domain"
| "consumer_domain"
| "blocked_domain"
| "unreachable"
| "provider_unavailable";Why there is nothing to report. A normal 200, not an error.
The distinction that matters for what you show and whether you retry:
unreachable is about the site - the home page did not answer - and may
succeed later. provider_unavailable is about Connect - our scanner is
down or unconfigured - and is worth retrying without telling the user
anything about their own site. no_domain, consumer_domain and
blocked_domain never succeed on retry.
Note what is not here: a low score. Every check failing is a successful scan and the most useful report this produces.
Type Alias: PricingModel
type PricingModel = "fixed" | "from" | "range" | "recurring" | "free" | "on_request";How a price is stated.
fixed- ₹4,999from- From ₹4,999range- ₹4,999 – ₹9,999recurring- ₹999 / monthfreeon_request- "Price on request"
Type Alias: ProductBundleSupply
type ProductBundleSupply = "composite" | "mixed";How a bundle is supplied, for Indian GST:
composite- a main item plus extras that naturally go with it (a camera with its mount and a year's support). Taxed at the principal component's rate and HSN/SAC; the principal iscomponents[0].mixed- separate items sold together for one price (a gift hamper). Taxed at the highest rate among the components.
Type Alias: ProductCreateInput
type ProductCreateInput = ProductInput & {
externalIds?: ProductExternalIds;
};POST /products: a ProductInput plus optional externalIds, the link to
another system's copy (an importer stamps crmPriceListItemId so a re-run
skips the row). Create-only: a PATCH carrying externalIds is refused.
Type Declaration
externalIds?
optional externalIds?: ProductExternalIds;Type Alias: ProductFieldAppliesTo
type ProductFieldAppliesTo = "all" | "product" | "service";Which kind of entry a custom field belongs on - a warranty on products, a
session length on services, or both ("all", what every field created
before this existed is).
Type Alias: ProductFieldType
type ProductFieldType =
| "text"
| "long_text"
| "number"
| "select"
| "multi_select"
| "boolean"
| "date"
| "url";Type Alias: ProductFieldValue
type ProductFieldValue = string | number | boolean | string[] | null;A custom field's value on a product.
Type Alias: ProductKind
type ProductKind = "product" | "service";The Products application's wire contract, and the pure helpers every host prints a price or builds a quote line with.
Hand-written, like types.ts: the published package carries no dependency
on the Products app or on Connect's internals. The shapes are
docs/design/products-app.md §1-§2, verbatim. Isomorphic - no React, no
Node-only code - so a Server Component, a route handler and a client
component all format a price the same way.
The UI is @cosmoops/connect-sdk/products; the relay a host mounts is
createProductsHandler in ./server / ./next.
Type Alias: ProductListSort
type ProductListSort = "name" | "updated";Type Alias: ProductPricingInterval
type ProductPricingInterval = "day" | "week" | "month" | "year";Type Alias: ProductsErrorCode
type ProductsErrorCode =
| "validation_error"
| "forbidden"
| "not_found"
| "conflict"
| "limit_reached"
| "unauthorized"
| "not_connected"
| "unconfigured"
| "upstream_error"
| "internal_error";Type Alias: ProductsScope
type ProductsScope = typeof PRODUCTS_SCOPES[keyof typeof PRODUCTS_SCOPES];Type Alias: ProductStatus
type ProductStatus = "active" | "draft" | "archived";Type Alias: ProductUpdateInput
type ProductUpdateInput = {
version: number;
} & Partial<ProductInput>;PATCH /products/{id} - the version read, plus whichever fields change.
Type Declaration
version
version: number;Type Alias: ReadinessAssessmentResult
type ReadinessAssessmentResult =
| {
status: "assessed";
report: ReadinessReport;
cached: boolean;
}
| {
status: "skipped";
reason: ReadinessSkipReason;
};Union Members
Type Literal
{
status: "assessed";
report: ReadinessReport;
cached: boolean;
}status
status: "assessed";report
report: ReadinessReport;cached
cached: boolean;Whether Connect served this from its platform-wide cache. The report is the same either way.
Type Literal
{
status: "skipped";
reason: ReadinessSkipReason;
}Type Alias: ReadinessBlockerCode
type ReadinessBlockerCode =
| "subscription_missing"
| "subscription_inactive"
| "audience_mismatch"
| "verification_required"
| "verification_pending"
| "verification_rejected"
| "verification_expired"
| "payment_method_missing"
| "seat_limit_reached"
| "workspace_not_found"
| "onboarding_approval_pending"
| "onboarding_approval_rejected"
| "workspace_suspended"
| "payment_retry_pending";Type Alias: ReadinessCheckKind
type ReadinessCheckKind = "core" | "bonus";Whether a check can cost points or only earn them.
A bonus check raises the score when it passes and leaves it alone when it
does not - so a site is never marked down for missing a convention almost
nobody has adopted yet. Worth surfacing differently in a UI: a failed core
check is a gap, an absent bonus one is an opportunity.
Type Alias: ReadinessCheckStatus
type ReadinessCheckStatus = "pass" | "partial" | "fail" | "absent" | "error";How a check came out.
error is the one to handle deliberately: it means Connect could not
measure this - a timeout, a refused connection - and it is excluded from
the score rather than counted as a failure. Render it as unknown, never as a
problem. absent ("you do not have this") and fail ("you have it and it is
broken") are likewise distinct and want different words.
Type Alias: ReadinessDimension
type ReadinessDimension =
| "agent_interfaces"
| "retrievability"
| "machine_readable"
| "crawl_consent"
| "trust";The five questions a readiness report answers.
Type Alias: ReadinessGrade
type ReadinessGrade = "A" | "B" | "C" | "D" | "F";Type Alias: ReadinessSkipReason
type ReadinessSkipReason =
| "no_domain"
| "blocked_domain"
| "consumer_domain"
| "unreachable"
| "provider_unavailable";Why there is nothing to report. A normal 200, not an error.
The distinction that matters for what you show and whether you retry:
unreachable is about the site - it did not answer - and may succeed
later. provider_unavailable is about Connect - our assessment service
is down or unconfigured - and is worth retrying without telling the user
anything about their own site. no_domain and blocked_domain never succeed
on retry.
Type Alias: SendingDomainRecordPurpose
type SendingDomainRecordPurpose = "ownership" | "dkim" | "mail_from_mx" | "mail_from_spf" | "dmarc";What a DNS record a sending domain asks for is for.
Type Alias: SendingDomainStatus
type SendingDomainStatus = "pending" | "verified" | "failing" | "failed";The lifecycle of a workspace's sending domain - see SendingDomain.
Type Alias: SessionDeviceType
type SessionDeviceType = "desktop" | "mobile" | "tablet" | "unknown";Type Alias: SetupStepState
type SetupStepState = "done" | "todo" | "skipped" | "in_review" | "blocked";Where one step stands, in precedence order (done wins over everything):
done- the data says so.blocked- a step it comesafteris not done yet, or the application reported ablockedBy. Nothing to press.in_review- submitted, waiting on somebody else (Meta, a KYB review).skipped- the admin pressed "Skip for now". Still unfinished - just not forced again.todo- open.
Type Alias: StoreAggregationOperation
type StoreAggregationOperation = "count" | "sum" | "avg";Type Alias: StoreCommitMode
type StoreCommitMode = "create" | "replace" | "merge" | "delete";Type Alias: StoreFieldTransform
type StoreFieldTransform =
| {
__transform: "increment";
by: number;
}
| {
__transform: "serverTimestamp";
}
| {
__transform: "arrayUnion";
values: unknown[];
}
| {
__transform: "arrayRemove";
values: unknown[];
}
| {
__transform: "delete";
};A value Connect computes at write time instead of storing verbatim.
Build them with the increment/serverTimestamp/arrayUnion/
arrayRemove/deleteField helpers rather than by hand - the sentinel shape
is an encoding, not an API.
increment is the one that cannot be expressed any other way: it is
commutative, so concurrent increments all land with no precondition and no
retry, which read-modify-write from your own process can never guarantee.
Type Alias: StoreIndexDirection
type StoreIndexDirection = "asc" | "desc" | "array-contains";How one field sits in a composite index. array-contains is a position, not an order.
Type Alias: StorePrecondition
type StorePrecondition =
| {
exists: boolean;
}
| {
updateTime: string;
};What must still be true of a document for a commit to land.
{ updateTime } is the _updateTime a read returned - the optimistic half
of read, decide, commit. A precondition that no longer holds fails the whole
commit with a 409 and writes nothing.
Type Alias: StoreQueryOperator
type StoreQueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains";The comparison operators a store query may use - narrower than Firestore's own set.
Type Alias: SubscriptionStatus
type SubscriptionStatus = "created" | "authenticated" | "active" | "paused" | "cancelled";Type Alias: TelephonyEdgeEvent
type TelephonyEdgeEvent = "answer" | "hangup" | "recording" | "fallback";Which carrier callback a relayed event is.
Type Alias: ThemeMode
type ThemeMode = "light" | "dark" | "system";Host-controlled - the SDK has no theme provider of its own. See ConnectUserMenuProps.theme.
Type Alias: ThumbnailSize
type ThumbnailSize = "small" | "medium" | "large";Type Alias: TypefaceRole
type TypefaceRole = "display" | "heading" | "body" | "mono";Type Alias: UpsertContactFromInteractionInput
type UpsertContactFromInteractionInput = ContactKey & {
workspaceId: string;
facts: ContactFactsInput;
source: "call" | "extraction";
callId?: string | null;
countCall?: boolean;
};Type Declaration
workspaceId
workspaceId: string;facts
facts: ContactFactsInput;source
source: "call" | "extraction";call for what your telephony knows, extraction for what a model inferred.
callId?
optional callId?: string | null;The call or conversation the facts came from. Recorded against each fact.
countCall?
optional countCall?: boolean;true only on the one write that closes the interaction. Defaults to
false, so a save made mid-call does not count the call again.
Type Alias: UpsertContactInput
type UpsertContactInput = ContactKey & {
workspaceId: string;
facts?: ContactFactsInput;
source?: "call" | "extraction" | "api";
callId?: string | null;
countCall?: boolean;
};contacts.upsert - find-or-create by phone or email, for a record your
application keeps (a helpdesk customer, a chat visitor who left an address).
Type Declaration
workspaceId
workspaceId: string;facts?
optional facts?: ContactFactsInput;source?
optional source?: "call" | "extraction" | "api";Who is claiming these facts. All three are automated sources: they fill
blanks and never overwrite what a person typed. Defaults to api.
callId?
optional callId?: string | null;countCall?
optional countCall?: boolean;See UpsertContactFromInteractionInput.countCall. Defaults to false.
Type Alias: VisitorField
type VisitorField = "message" | "email" | "code" | "name";What the widget should ask for next, decided by Connect.
"message" is the ordinary composer. The others mean render that field's own
input instead - and the reason this is a server decision rather than something
the widget infers from the assistant's sentence is the phone: an email asked
for in prose gets typed into a general text box with autocorrect on, where
type="email" inputmode="email" autocomplete="email" brings up a keyboard
with an @ on it. A six-digit code wants autocomplete="one-time-code",
which is what lets iOS offer the code straight from the notification.
Type Alias: VisitorKnowledgeIndexState
type VisitorKnowledgeIndexState = "inline" | "indexing" | "indexed" | "failed";How a published item reaches the model.
inline is the normal and better state for a small knowledge base, not a
degraded one: the model is shown every published item rather than a top-k
slice, with no chunk boundaries and no retrieval round trip. The other three
only occur once a knowledge base has outgrown the prompt.
Type Alias: VisitorKnowledgeSource
type VisitorKnowledgeSource = "website" | "authored" | "upload" | "support";Where a knowledge item's text came from.
Type Alias: VisitorMessageRole
type VisitorMessageRole = "visitor" | "assistant" | "agent";Who wrote a visitor message. agent is a person answering through
visitor.sessions.appendAgentMessage while a hand-off holds the
conversation - never the model.
Type Alias: WhatsAppTemplateReviewStatus
type WhatsAppTemplateReviewStatus = "DRAFT" | "PENDING" | "APPROVED" | "REJECTED" | "PAUSED" | "DISABLED";Meta's review state for a template. Only APPROVED sends.
Type Alias: WirePlanFeature
type WirePlanFeature = string | PlanFeature;What features can look like coming off the wire.
Connect used to send bare keys and now sends { key, name, description }.
A published SDK talks to whatever version the partner's Connect instance is
running and has no say in when that moves - so both shapes are real, and a
client that assumes the newer one renders an empty feature list against
every instance that has not upgraded yet.
Type Alias: WorkspaceAppCategory
type WorkspaceAppCategory =
| "communication"
| "storage"
| "analytics"
| "security"
| "billing"
| "productivity"
| "sales"
| "operations"
| "infrastructure"
| "other";Connect's closed set of catalog categories - WorkspaceApp.category.
Type Alias: WorkspaceType
type WorkspaceType = "individual" | "business";Variables
Variable: ACCESS_TOKEN_TYPE_URN
const ACCESS_TOKEN_TYPE_URN: "urn:ietf:params:oauth:token-type:access_token" = "urn:ietf:params:oauth:token-type:access_token";RFC 8693 - the subject_token_type of a member's session token.
Variable: ANALYTICS_PERIOD_LABELS
const ANALYTICS_PERIOD_LABELS: Readonly<Record<
| AnalyticsPeriodPreset
| "custom", string>>;Variable: ANALYTICS_PERIOD_PRESETS
const ANALYTICS_PERIOD_PRESETS: readonly ["today", "yesterday", "last_7_days", "last_14_days", "last_30_days", "last_90_days", "this_week", "last_week", "this_month", "last_month", "this_quarter", "this_year"];Variable: APPLICATION_EVENT_WEBHOOK_TYPE
const APPLICATION_EVENT_WEBHOOK_TYPE: "application.event" = "application.event";The webhook event type that wraps every application-published event.
Variable: APPLICATIONS_CALL_SCOPE
const APPLICATIONS_CALL_SCOPE: "applications:call" = "applications:call";The API-key scope a consumer needs to ask for a token at all.
Variable: CONNECT_TOKEN_REFRESH_SKEW_SECONDS
const CONNECT_TOKEN_REFRESH_SKEW_SECONDS: 60 = 60;The SDK refreshes a cached token this long before exp, so one is never presented in its last minute.
Variable: CONNECT_TOKEN_TTL_SECONDS
const CONNECT_TOKEN_TTL_SECONDS: 300 = 300;A token's lifetime as Connect issues it.
Variable: CONNECT_TOKEN_TYPE
const CONNECT_TOKEN_TYPE: "connect-app+jwt" = "connect-app+jwt";The JWT typ header of a Connect application token. Anything else is refused.
Variable: DEFAULT_ANALYTICS_TIME_ZONE
const DEFAULT_ANALYTICS_TIME_ZONE: "Asia/Kolkata" = "Asia/Kolkata";The zone Connect defaults a workspace to, and the fallback for an unknown one.
Variable: EMPTY_SETUP_RECORD
const EMPTY_SETUP_RECORD: SetupRecord;A workspace that has never opened the wizard.
Variable: EVENTS_PUBLISH_SCOPE
const EVENTS_PUBLISH_SCOPE: "events:publish" = "events:publish";The API-key scope a provider needs to publish events.
Variable: MAX_ANALYTICS_PERIOD_DAYS
const MAX_ANALYTICS_PERIOD_DAYS: 400 = 400;The longest range analytics.metrics() reads, in days.
Variable: NO_PLAN_LIMIT
const NO_PLAN_LIMIT: PlanLimit;What a key the plan carries no answer for reads as - see
SubscribedPlan.limits, which is sparse.
"none" rather than "unlimited": a plan that was never asked about a
limit has not sold it, and defaulting the other way would hand an unbounded
allowance to every tier that predates the question.
Variable: ONBOARDING_DONE_PATH
const ONBOARDING_DONE_PATH: "/onboarding/done" = "/onboarding/done";The wizard's last screen: what was set up, and the try-it actions. Where /onboarding lands once nothing is open.
Variable: ONBOARDING_PATH
const ONBOARDING_PATH: "/onboarding" = "/onboarding";The wizard's root. GET /onboarding resolves to a step (or the waiting page) - it has no page of its own.
Variable: ONBOARDING_WAITING_PATH
const ONBOARDING_WAITING_PATH: "/onboarding/waiting" = "/onboarding/waiting";"Your admin is setting up <app>" - where a member without setup permission is sent.
Variable: PRICING_INTERVAL_LABELS
const PRICING_INTERVAL_LABELS: Readonly<Record<ProductPricingInterval, string>>;The Products application's wire contract and its pure helpers - a price as
one line of text, and a catalogue entry as a tax-exclusive quote line. The
UI is ./products; the relay is createProductsHandler in ./server.
Variable: PRICING_MODEL_LABELS
const PRICING_MODEL_LABELS: Readonly<Record<PricingModel, string>>;The Products application's wire contract and its pure helpers - a price as
one line of text, and a catalogue entry as a tax-exclusive quote line. The
UI is ./products; the relay is createProductsHandler in ./server.
Variable: PRODUCT_BUNDLE_SUPPLY_HELP
const PRODUCT_BUNDLE_SUPPLY_HELP: Readonly<Record<ProductBundleSupply, string>>;One plain-English line per supply kind - what a form shows under the choice.
Variable: PRODUCT_BUNDLE_SUPPLY_LABELS
const PRODUCT_BUNDLE_SUPPLY_LABELS: Readonly<Record<ProductBundleSupply, string>>;The Products application's wire contract and its pure helpers - a price as
one line of text, and a catalogue entry as a tax-exclusive quote line. The
UI is ./products; the relay is createProductsHandler in ./server.
Variable: PRODUCT_KIND_LABELS
const PRODUCT_KIND_LABELS: Readonly<Record<ProductKind, string>>;The Products application's wire contract and its pure helpers - a price as
one line of text, and a catalogue entry as a tax-exclusive quote line. The
UI is ./products; the relay is createProductsHandler in ./server.
Variable: PRODUCT_LIMITS
const PRODUCT_LIMITS: {
productsPerWorkspace: 5000;
customFields: 50;
images: 12;
imageBytes: number;
imageTypes: readonly string[];
tags: 20;
tagLength: 40;
nameLength: 160;
skuLength: 64;
summaryLength: 280;
descriptionLength: 20000;
pricingDescriptionLength: 280;
bundleMinComponents: 2;
bundleMaxComponents: 20;
bundleMaxQuantity: 100000;
};Constants, not plan limits - Products has no plan.
Type Declaration
productsPerWorkspace
readonly productsPerWorkspace: 5000 = 5_000;customFields
readonly customFields: 50 = 50;images
readonly images: 12 = 12;imageBytes
readonly imageBytes: number;imageTypes
readonly imageTypes: readonly string[];tags
readonly tags: 20 = 20;tagLength
readonly tagLength: 40 = 40;nameLength
readonly nameLength: 160 = 160;skuLength
readonly skuLength: 64 = 64;summaryLength
readonly summaryLength: 280 = 280;descriptionLength
readonly descriptionLength: 20000 = 20_000;pricingDescriptionLength
readonly pricingDescriptionLength: 280 = 280;bundleMinComponents
readonly bundleMinComponents: 2 = 2;bundleMaxComponents
readonly bundleMaxComponents: 20 = 20;bundleMaxQuantity
readonly bundleMaxQuantity: 100000 = 100_000;Variable: PRODUCTS_SCOPES
const PRODUCTS_SCOPES: {
read: "products:catalog.read";
write: "products:catalog.write";
};The scopes Products exposes. CRM and Support consume both.
Type Declaration
read
readonly read: "products:catalog.read" = "products:catalog.read";write
readonly write: "products:catalog.write" = "products:catalog.write";Variable: TOKEN_EXCHANGE_GRANT_TYPE
const TOKEN_EXCHANGE_GRANT_TYPE: "urn:ietf:params:oauth:grant-type:token-exchange" = "urn:ietf:params:oauth:grant-type:token-exchange";RFC 8693 - the grant tokenForMember uses.
Functions
Function: arrayRemove()
function arrayRemove(...values: unknown[]): StoreFieldTransform;Removes every occurrence of each value.
Parameters
| Parameter | Type |
|---|---|
...values | unknown[] |
Returns
Function: arrayUnion()
function arrayUnion(...values: unknown[]): StoreFieldTransform;Adds the values not already in the array. Order is not preserved.
Parameters
| Parameter | Type |
|---|---|
...values | unknown[] |
Returns
Function: blockingBlockers()
function blockingBlockers(readiness: ReadinessResponse): ReadinessBlocker[];The blockers that actually deny access, in the order to show them.
Advisory ones (no saved payment method mid-subscription, say) are worth surfacing but are not a reason to lock anyone out today - filtering them here keeps that judgement in one place rather than in every call site.
Parameters
| Parameter | Type |
|---|---|
readiness | ReadinessResponse |
Returns
Function: bundleViewTax()
function bundleViewTax(bundle: ProductBundleView): BundleTax;deriveBundleTax over a bundle as a read returns it; unreadable components are left out.
Parameters
| Parameter | Type |
|---|---|
bundle | ProductBundleView |
Returns
Function: deleteField()
function deleteField(): StoreFieldTransform;Removes the field entirely.
merge only - a replace already drops every field it does not list, so
asking it to delete one is a contradiction rather than a narrowing.
Returns
Function: deriveBundleTax()
function deriveBundleTax(supply: ProductBundleSupply, components: readonly BundleTaxSource[]): BundleTax;A bundle's GST rate and HSN/SAC - the rule Products applies on save, re-implemented here (the package carries no dependency on the app) and held to the same answers by the Products app's conformance test.
composite: the principal's (components[0]) rate and code. Only the principal counts, so a missing rate on an extra does not blank it.mixed: the highest rate, with that component's code (first on a tie). If any component states no rate the highest is unknowable:null.
Parameters
| Parameter | Type |
|---|---|
supply | ProductBundleSupply |
components | readonly BundleTaxSource[] |
Returns
Function: deriveSetupProgress()
function deriveSetupProgress<K>(
definitions: readonly SetupStepDefinition<K>[],
signals: Partial<Record<K, SetupSignal>>,
record: SetupRecord): SetupProgress<K>;The whole onboarding state from three inputs: the application's step list, what its data says, and the stored record.
- A missing signal is
todo, and a step that is notdonenever counts as done - absence is never progress. - State precedence is
done>blocked>in_review>skipped>todo. A skip on a step that is not skippable is ignored. - The lock latches.
requiredCompleteis true once every required step is done orrecord.requiredCompletedAtis set;shouldLatchtells the application to write it. A workspace that deletes its only email channel after setup sees the step come back astodoon the checklist - and is not locked out of the app for it. nextis where/onboardingresumes: the first required step not done and not blocked; else the first step neither done, skipped nor blocked (an optional step a blocked one waits on, say); else the first required step not done even though blocked, so a locked workspace is never sent to the done screen; elsenull.
Type Parameters
| Type Parameter |
|---|
K extends string |
Parameters
| Parameter | Type |
|---|---|
definitions | readonly SetupStepDefinition<K>[] |
signals | Partial<Record<K, SetupSignal>> |
record | SetupRecord |
Returns
Function: describeQueryRejection()
function describeQueryRejection(collection: StoreCollectionDeclaration, query: {
filters?: readonly StoreQueryFilter[];
orderBy?: StoreOrderBy | null;
}): string | null;Why a collection cannot serve a query, in words a caller can act on - or
null when it can.
Runs the same two rules Connect runs, in the same order: every field must be declared queryable, and the shape must be inside an automatic single-field index or covered by a declared composite one. Checking here rather than letting the call fail is not about saving a round trip - it is so the refusal can name the index to declare while the caller still has the query in hand.
Parameters
| Parameter | Type |
|---|---|
collection | StoreCollectionDeclaration |
query | { filters?: readonly StoreQueryFilter[]; orderBy?: StoreOrderBy | null; } |
query.filters? | readonly StoreQueryFilter[] |
query.orderBy? | StoreOrderBy | null |
Returns
string | null
Function: documentExists()
function documentExists(exists?: boolean): StorePrecondition;"Only if this document exists" / "only if it does not."
Parameters
| Parameter | Type | Default value |
|---|---|---|
exists | boolean | true |
Returns
Function: formatInrPaise()
function formatInrPaise(paise: number): string;Paise as rupees: 499900 → "₹4,999", 499950 → "₹4,999.50".
Whole rupees print without decimals; anything else prints two.
Parameters
| Parameter | Type |
|---|---|
paise | number |
Returns
string
Function: formatMetricValue()
function formatMetricValue(value: number | null, unit?: string | null): string;A metric value as text, by its unit. paise is rupees (compact above a
lakh), bytes is KB/MB/GB, ms is seconds or minutes; anything else is a
grouped integer. null is an en dash - not "0", which would claim a
measurement.
Parameters
| Parameter | Type |
|---|---|
value | number | null |
unit? | string | null |
Returns
string
Function: formatProductPrice()
function formatProductPrice(pricing: ProductPricing, options?: FormatProductPriceOptions): string;One line for a price: "₹4,999", "From ₹4,999", "₹4,999 – ₹9,999",
"₹999 / month", "₹500 / hour", "Free", "Price on request".
The MRP is deliberately not included - a strike-through is a second
element beside this one (<ProductPrice> draws it), not text inside it.
Neither is the free-text pricing.description. A priced model whose amount
is missing reads as "Price on request" rather than "₹0", which would be a
price nobody set. A range without a maximum reads as from.
Parameters
| Parameter | Type |
|---|---|
pricing | ProductPricing |
options | FormatProductPriceOptions |
Returns
string
Function: increment()
function increment(by: number): StoreFieldTransform;Add to a number, atomically.
The one operation you genuinely cannot do yourself. Increments are
commutative, so two concurrent increment(500) calls both land - where
read-add-write from your own process loses one of them however carefully it
retries, because the read and the write are two round trips with a gap in
between. Use this for balances, counters and tallies, and reach for a
precondition only when the new value depends on the old one in a way
addition cannot express.
Negative to subtract; there is no separate decrement.
Parameters
| Parameter | Type |
|---|---|
by | number |
Returns
Function: isConnectError()
function isConnectError(error: unknown): error is ConnectError;@cosmoops/connect-sdk - the official client for the CosmoOps Connect
application-partner API (/api/v1/application/*).
This root entry is the isomorphic surface: wire types, the error class, and pure helpers. It pulls in neither Node-only code nor React, so it is safe to import from anywhere.
@cosmoops/connect-sdk/server-ConnectClientandcreateConnectHandler. Holds the API key. Never import this from browser code.@cosmoops/connect-sdk/client- React components and hooks. Talks to your own origin only.@cosmoops/connect-sdk/mcp- your store's declaration, derived into MCP tools. Holds the API key; never import this from browser code.
Parameters
| Parameter | Type |
|---|---|
error | unknown |
Returns
error is ConnectError
Function: isLocalDay()
function isLocalDay(value: unknown): value is string;A real YYYY-MM-DD calendar day.
Parameters
| Parameter | Type |
|---|---|
value | unknown |
Returns
value is string
Function: isOnboardingPath()
function isOnboardingPath(pathname: string): boolean;Whether a pathname is inside the wizard - the lock's exemption. /onboarding
and anything under /onboarding/, never /onboardingx. A query string or
hash is ignored, so a raw request.url path works too.
Parameters
| Parameter | Type |
|---|---|
pathname | string |
Returns
boolean
Function: isSetupLocked()
function isSetupLocked(progress: Pick<SetupProgress, "requiredComplete">): boolean;Whether the application must keep this workspace on /onboarding.
Parameters
| Parameter | Type |
|---|---|
progress | Pick<SetupProgress, "requiredComplete"> |
Returns
boolean
Function: localDayCount()
function localDayCount(from: string, to: string): number;Inclusive number of days from from to to (1 for the same day).
Parameters
| Parameter | Type |
|---|---|
from | string |
to | string |
Returns
number
Function: localDayIn()
function localDayIn(at: Date, timeZone: string): string;The local day at falls on in timeZone. Built from formatToParts, never
a locale string, so it does not depend on the engine's locale data - the
same rule Connect's own localDayOf follows, which is what stamps an
event's day at ingest.
Parameters
| Parameter | Type |
|---|---|
at | Date |
timeZone | string |
Returns
string
Function: metricDelta()
function metricDelta(current: number | null, previous: number | null): MetricDelta | null;The change from previous to current, or null when either is not
measured - a null metric value is "not measured yet", and a delta against
it would read as a measured result.
Parameters
| Parameter | Type |
|---|---|
current | number | null |
previous | number | null |
Returns
MetricDelta | null
Function: monthStartOf()
function monthStartOf(day: string): string;The 1st of day's month - the start of a month bucket.
Parameters
| Parameter | Type |
|---|---|
day | string |
Returns
string
Function: nextSetupPath()
function nextSetupPath<K>(definitions: readonly SetupStepDefinition<K>[], key: K): string;The step after key in the list, or the done screen after the last (and for a key not in the list).
Type Parameters
| Type Parameter |
|---|
K extends string |
Parameters
| Parameter | Type |
|---|---|
definitions | readonly SetupStepDefinition<K>[] |
key | K |
Returns
string
Function: normalizePlanFeatures()
function normalizePlanFeatures(features:
| readonly WirePlanFeature[]
| undefined): PlanFeature[];Coerces either wire shape into PlanFeature[].
A bare key becomes { key, name: key, description: "" } - the same
fallback the server applies for a plan older than its application's feature
catalog, so an un-upgraded Connect renders exactly what it always did
rather than a column of blanks.
Applied inside BillingResource.listPlans, so anything reading a plan
through the SDK or its bridge already has the normalised shape and no
component needs to know this ambiguity exists. Exported for a partner who
hand-rolled a bridge instead of using createConnectHandler.
Parameters
| Parameter | Type |
|---|---|
features | | readonly WirePlanFeature[] | undefined |
Returns
Function: normalizeSetupRecord()
function normalizeSetupRecord(raw: unknown): SetupRecord;A stored record, read tolerantly: a missing document, a hand-edited one, or one written before a field existed all read as something usable rather than throwing in a layout. Unknown fields are dropped, non-string skipped keys are dropped, duplicates collapse.
Parameters
| Parameter | Type |
|---|---|
raw | unknown |
Returns
Function: parsePeriod()
function parsePeriod(
params: {
period?: string | null;
from?: string | null;
to?: string | null;
},
timeZone: string,
options?: {
now?: Date;
fallback?: | "today"
| "yesterday"
| "last_7_days"
| "last_14_days"
| "last_30_days"
| "last_90_days"
| "this_week"
| "last_week"
| "this_month"
| "last_month"
| "this_quarter"
| "this_year";
maxDays?: number;
}): AnalyticsPeriod;?period= or ?from=&to= -> a period, the way a report page reads its URL.
A custom range wins when both ends are real days; it is put in order,
clamped to end no later than today, and to at most maxDays (the start
moves, because "up to today" is what somebody picking an end date meant).
Anything unreadable falls back to fallback rather than refusing - a
mistyped link should still show a report.
Parameters
| Parameter | Type |
|---|---|
params | { period?: string | null; from?: string | null; to?: string | null; } |
params.period? | string | null |
params.from? | string | null |
params.to? | string | null |
timeZone | string |
options | { now?: Date; fallback?: | "today" | "yesterday" | "last_7_days" | "last_14_days" | "last_30_days" | "last_90_days" | "this_week" | "last_week" | "this_month" | "last_month" | "this_quarter" | "this_year"; maxDays?: number; } |
options.now? | Date |
options.fallback? | | "today" | "yesterday" | "last_7_days" | "last_14_days" | "last_30_days" | "last_90_days" | "this_week" | "last_week" | "this_month" | "last_month" | "this_quarter" | "this_year" |
options.maxDays? | number |
Returns
Function: periodGranularity()
function periodGranularity(days: number): "month" | "day" | "week";The granularity a chart over days reads: one column per day up to a
month, per week up to a quarter-and-a-bit, per month beyond.
Parameters
| Parameter | Type |
|---|---|
days | number |
Returns
"month" | "day" | "week"
Function: periodSearchParams()
function periodSearchParams(period: AnalyticsPeriod): Record<string, string>;?period= or ?from=&to= for a link that keeps the current period.
Parameters
| Parameter | Type |
|---|---|
period | AnalyticsPeriod |
Returns
Record<string, string>
Function: planLimitAllows()
function planLimitAllows(
limit: PlanLimit,
currentCount: number,
additional?: number): boolean;Whether additional more of the resource fit under this limit, given how
many the subscriber already has. The comparison, written once.
Every way of writing it at a call site is wrong in a different direction:
count < limit.value is off by one as soon as you add more than one row,
if (!limit.value) refuses the unlimited tier, and
limit.value ?? Infinity grants unlimited to a plan that allows none. This
is the same implementation Connect's own server uses to validate a plan.
const limit = await connect.entitlements.getLimit(workspaceId, "sites");
if (!planLimitAllows(limit, await countSites(workspaceId))) {
throw new Error("Your plan's site limit is reached.");
}Parameters
| Parameter | Type | Default value |
|---|---|---|
limit | PlanLimit | undefined |
currentCount | number | undefined |
additional | number | 1 |
Returns
boolean
Function: planLimitFor()
function planLimitFor(limits:
| Readonly<Record<string, PlanLimit>>
| undefined, key: string): PlanLimit;One plan's answer for one limit key, with the sparse-map default applied.
Indexing plan.limits directly is what makes a missing key undefined,
which then reads as unlimited through the first ?. somebody writes. This
returns a PlanLimit either way.
Parameters
| Parameter | Type |
|---|---|
limits | | Readonly<Record<string, PlanLimit>> | undefined |
key | string |
Returns
Function: prevSetupPath()
function prevSetupPath<K>(definitions: readonly SetupStepDefinition<K>[], key: K): string | null;The step before key, or null on the first (and for a key not in the list).
Type Parameters
| Type Parameter |
|---|
K extends string |
Parameters
| Parameter | Type |
|---|---|
definitions | readonly SetupStepDefinition<K>[] |
key | K |
Returns
string | null
Function: productFieldAppliesTo()
function productFieldAppliesTo(field: Pick<ProductField, "appliesTo">, kind: ProductKind): boolean;Whether a form or page for a kind entry should offer field. A field
from a Products deployment that predates appliesTo arrives without it and
applies to both, as it always did.
Parameters
| Parameter | Type |
|---|---|
field | Pick<ProductField, "appliesTo"> |
kind | ProductKind |
Returns
boolean
Function: productQuoteLine()
function productQuoteLine(product: Pick<Product, "id" | "name" | "summary" | "pricing">): ProductQuoteLine;The quote line a catalogue entry becomes.
from/rangequote their lower bound;recurringquotes one interval.- A tax-inclusive price is converted to exclusive with
taxExclusiveFromInclusive(integer paise,Math.round), so adding the line's GST back reproduces the catalogue price to the paisa. on_requestor a missing amount gives0withamountMissing: true.
Parameters
| Parameter | Type |
|---|---|
product | Pick<Product, "id" | "name" | "summary" | "pricing"> |
Returns
Function: productTaxNote()
function productTaxNote(pricing: ProductPricing): string | null;The tax note under a price: "Incl. 18% GST", "+ 18% GST", or null
when the rate is not stated (or the price is free / on request).
Parameters
| Parameter | Type |
|---|---|
pricing | ProductPricing |
Returns
string | null
Function: requiredRecordsOutstanding()
function requiredRecordsOutstanding(domain: Pick<SendingDomain, "records">): SendingDomainRecord[];The required DNS records a sending domain is still waiting on - every
required record whose last check was not ok. Empty means nothing
required is outstanding; recommended records (the return path, DMARC) are
never listed here, because leaving them out does not block verification.
Parameters
| Parameter | Type |
|---|---|
domain | Pick<SendingDomain, "records"> |
Returns
Function: resolvePeriod()
function resolvePeriod(
preset:
| "today"
| "yesterday"
| "last_7_days"
| "last_14_days"
| "last_30_days"
| "last_90_days"
| "this_week"
| "last_week"
| "this_month"
| "last_month"
| "this_quarter"
| "this_year",
timeZone: string,
now?: Date): AnalyticsPeriod;A named period ending (or lying) before now in timeZone.
To-date periods end today, which Connect measures from live counters -
so the latest bucket of a this_month read moves through the day.
Parameters
| Parameter | Type |
|---|---|
preset | | "today" | "yesterday" | "last_7_days" | "last_14_days" | "last_30_days" | "last_90_days" | "this_week" | "last_week" | "this_month" | "last_month" | "this_quarter" | "this_year" |
timeZone | string |
now | Date |
Returns
Function: safeTimeZone()
function safeTimeZone(timeZone: string | null | undefined): string;timeZone if the runtime knows it, else the default - a stored zone is data, not a guarantee.
Parameters
| Parameter | Type |
|---|---|
timeZone | string | null | undefined |
Returns
string
Function: sendingDomainMaySend()
function sendingDomainMaySend(domain: Pick<SendingDomain, "status">): boolean;Whether a sending domain may carry SendEmailInput.fromAddress right now -
the same rule Connect's send path applies. verified sends, and so does
failing: the provider is still signing, and that is the grace period
before the domain becomes failed. pending and failed do not.
Parameters
| Parameter | Type |
|---|---|
domain | Pick<SendingDomain, "status"> |
Returns
boolean
Function: serverTimestamp()
function serverTimestamp(): StoreFieldTransform;Connect's own write time, rather than a clock reading from your process.
Returns
Function: setupResumePath()
function setupResumePath(progress: Pick<SetupProgress, "next">): string;Where GET /onboarding sends somebody who may set the application up.
Parameters
| Parameter | Type |
|---|---|
progress | Pick<SetupProgress, "next"> |
Returns
string
Function: shiftLocalDay()
function shiftLocalDay(day: string, delta: number): string;day moved by delta days. Calendar arithmetic, no zone involved.
Parameters
| Parameter | Type |
|---|---|
day | string |
delta | number |
Returns
string
Function: storeIndexCovers()
function storeIndexCovers(declared: readonly {
fields: StoreIndexField[];
}[], required: readonly StoreIndexField[]): boolean;Whether one of declared can serve a query needing exactly required.
Parameters
| Parameter | Type |
|---|---|
declared | readonly { fields: StoreIndexField[]; }[] |
required | readonly StoreIndexField[] |
Returns
boolean
Function: storeIndexFor()
function storeIndexFor(query: {
filters?: readonly StoreQueryFilter[];
orderBy?: StoreOrderBy | null;
}): StoreIndexField[] | null;The composite index a query shape needs, or null when Firestore's
automatic single-field indexes already serve it.
Use it to generate your Store tab declarations from the queries your code actually makes, rather than transcribing them by hand and discovering the gap in production:
const needed = MY_QUERY_SHAPES.map(storeIndexFor).filter(Boolean);Field order is Firestore's, not yours. Equality fields first (sorted, so
the order you passed them in cannot produce two different declarations for
one query), then the array-contains field, then the field the query orders
by. Declaring the same shape twice in two orders builds two indexes, one of
which is dead weight on every document write.
Connect runs the same derivation server-side and is the authority: a query with no matching declared index is refused with the index it needs spelled out, so a disagreement here surfaces on the first call rather than silently.
Throws for a query Firestore cannot serve at all - two array-contains
filters, ranges over two fields, or a range ordered by a different field.
A range bounded on both sides over one field needs no declaration: two
filters, one field, and the automatic single-field index serves it. Ask this
function with the filters you actually send rather than with one of the two
bounds - the answer is the same, and modelling the range as a single >=
is how a schema comes to declare a shape its own runtime does not make.
Parameters
| Parameter | Type |
|---|---|
query | { filters?: readonly StoreQueryFilter[]; orderBy?: StoreOrderBy | null; } |
query.filters? | readonly StoreQueryFilter[] |
query.orderBy? | StoreOrderBy | null |
Returns
StoreIndexField[] | null
Function: storeIndexSignature()
function storeIndexSignature(fields: readonly StoreIndexField[]): string;An index as one comparable string - "status:asc,createdAt:desc".
Deduplicate a generated list on this before declaring it: a dozen query shapes across a screen usually collapse to two or three distinct indexes.
Parameters
| Parameter | Type |
|---|---|
fields | readonly StoreIndexField[] |
Returns
string
Function: taxExclusiveFromInclusive()
function taxExclusiveFromInclusive(inclusivePaise: number, ratePercent: number): number;The taxable value inside a tax-inclusive price, in integer paise:
round(inclusive * 100 / (100 + rate)).
Math.round, which is what Connect's taxableFromInclusive and the CRM's
own copy of it do: the paisa either way has to land somewhere, and rounding
the taxable value means the tax is whatever is left - so the two always add
back to the price on the shelf.
Parameters
| Parameter | Type |
|---|---|
inclusivePaise | number |
ratePercent | number |
Returns
number
Function: unchangedSince()
function unchangedSince(document: StoreDocument): StorePrecondition;"Only if this document has not changed since I read it."
Pass the document a read returned, and the precondition is built from its
_updateTime. Taking the document rather than the timestamp is deliberate:
a caller who has to reach for doc.data._updateTime themselves is one typo
away from a precondition that silently does nothing.
Throws rather than degrading when the document carries no _updateTime -
that only happens for a document you built yourself rather than read, and a
precondition that quietly became "no precondition" is worse than a crash at
the call site.
Parameters
| Parameter | Type |
|---|---|
document | StoreDocument |
Returns
Function: validateSetupDefinitions()
function validateSetupDefinitions(definitions: readonly SetupStepDefinition<string>[]): string[];Every problem with a step list, as sentences - an empty array means it is
sound. deriveSetupProgress throws on any of them, because a step list is a
constant and a broken one should fail the first test that reads it rather
than lock a workspace on a step that can never be reached.
Parameters
| Parameter | Type |
|---|---|
definitions | readonly SetupStepDefinition<string>[] |
Returns
string[]
Function: weekStartOf()
function weekStartOf(day: string): string;The Monday of day's week - the start of a week bucket.
Parameters
| Parameter | Type |
|---|---|
day | string |
Returns
string