diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 8cb22d94449..8d3a0f2b273 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -355,6 +355,7 @@ export const blockTypeToIconMap: Record = { arxiv: ArxivIcon, asana: AsanaIcon, ashby: AshbyIcon, + ashby_v2: AshbyIcon, athena: AthenaIcon, athena_v2: AthenaIcon, attio: AttioIcon, @@ -374,9 +375,11 @@ export const blockTypeToIconMap: Record = { buffer_v2: BufferIcon, calcom: CalComIcon, calendly: CalendlyIcon, + calendly_v2: CalendlyIcon, cbinsights: CbInsightsIcon, chat_trigger: Task, checkr: CheckrIcon, + checkr_v2: CheckrIcon, circleback: CirclebackIcon, circleback_v2: CirclebackIcon, clay: ClayIcon, @@ -449,6 +452,7 @@ export const blockTypeToIconMap: Record = { extend_v2: ExtendIcon, extend_v3: ExtendIcon, fathom: FathomIcon, + fathom_v2: FathomIcon, file: DocumentIcon, file_v2: DocumentIcon, file_v3: DocumentIcon, @@ -472,6 +476,7 @@ export const blockTypeToIconMap: Record = { github: GithubIcon, github_v2: GithubIcon, gitlab: GitLabIcon, + gitlab_v2: GitLabIcon, gmail: GmailIcon, gmail_v2: GmailIcon, gong: GongIcon, @@ -503,7 +508,9 @@ export const blockTypeToIconMap: Record = { grafana: GrafanaIcon, grain: GrainIcon, grain_v2: GrainIcon, + grain_v3: GrainIcon, granola: GranolaIcon, + granola_v2: GranolaIcon, greenhouse: GreenhouseIcon, greenhouse_v2: GreenhouseIcon, greptile: GreptileIcon, @@ -558,6 +565,7 @@ export const blockTypeToIconMap: Record = { launchdarkly_v2: LaunchDarklyIcon, leadmagic: LeadMagicIcon, lemlist: LemlistIcon, + lemlist_v2: LemlistIcon, linear: LinearIcon, linear_v2: LinearIcon, linkedin: LinkedInIcon, @@ -622,6 +630,7 @@ export const blockTypeToIconMap: Record = { otter_v2: OtterIcon, outlook: OutlookIcon, pagerduty: PagerDutyIcon, + pagerduty_v2: PagerDutyIcon, parallel_ai: ParallelIcon, peopledatalabs: PeopleDataLabsIcon, perplexity: PerplexityIcon, @@ -663,6 +672,7 @@ export const blockTypeToIconMap: Record = { reducto_v2: ReductoIcon, reducto_v3: ReductoIcon, resend: ResendIcon, + resend_v2: ResendIcon, response: ResponseIcon, revenuecat: RevenueCatIcon, rippling: RipplingIcon, @@ -670,6 +680,7 @@ export const blockTypeToIconMap: Record = { rocketlane: RocketlaneIcon, rocketlane_v2: RocketlaneIcon, rootly: RootlyIcon, + rootly_v2: RootlyIcon, router: ConnectIcon, router_v2: ConnectIcon, rss: RssIcon, @@ -688,6 +699,7 @@ export const blockTypeToIconMap: Record = { sendgrid: SendgridIcon, sendgrid_v2: SendgridIcon, sentry: SentryIcon, + sentry_v2: SentryIcon, serper: SerperIcon, servicenow: ServiceNowIcon, servicenow_v2: ServiceNowIcon, @@ -725,6 +737,7 @@ export const blockTypeToIconMap: Record = { start_trigger: StartIcon, starter: StartIcon, stripe: StripeIcon, + stripe_v2: StripeIcon, sts: STSIcon, sts_v2: STSIcon, stt: STTIcon, @@ -737,6 +750,7 @@ export const blockTypeToIconMap: Record = { tavily: TavilyIcon, tavily_v2: TavilyIcon, telegram: TelegramIcon, + telegram_v2: TelegramIcon, temporal: TemporalIcon, textract: TextractIcon, textract_v2: TextractIcon, @@ -755,12 +769,14 @@ export const blockTypeToIconMap: Record = { twilio_sms: TwilioIcon, twilio_voice: TwilioIcon, typeform: TypeformIcon, + typeform_v2: TypeformIcon, upstash: UpstashIcon, uptimerobot: UptimeRobotIcon, uptimerobot_v2: UptimeRobotIcon, vanta: VantaIcon, variables: VariableIcon, vercel: VercelIcon, + vercel_v2: VercelIcon, video_generator: VideoIcon, video_generator_v2: VideoIcon, video_generator_v3: VideoIcon, @@ -771,6 +787,7 @@ export const blockTypeToIconMap: Record = { webflow: WebflowIcon, webhook_request: WebhookIcon, whatsapp: WhatsAppIcon, + whatsapp_v2: WhatsAppIcon, wikipedia: WikipediaIcon, windchill: WindchillIcon, wiza: WizaIcon, diff --git a/apps/docs/content/docs/integrations/ashby.mdx b/apps/docs/content/docs/integrations/ashby.mdx index 97b66423074..ba0a259f5d0 100644 --- a/apps/docs/content/docs/integrations/ashby.mdx +++ b/apps/docs/content/docs/integrations/ashby.mdx @@ -2,7 +2,7 @@ title: Ashby description: Manage candidates, jobs, and applications in Ashby integration: - type: "ashby" + type: "ashby_v2" color: "#5D4ED6" --- @@ -63,7 +63,6 @@ Adds a tag to a candidate in Ashby and returns the updated candidate. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | The UUID of the candidate to add the tag to | | `tagId` | string | Yes | The UUID of the tag to add | @@ -122,7 +121,6 @@ Remove personally identifiable information from an Ashby candidate while retaini | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | UUID of the candidate to anonymize | @@ -180,7 +178,6 @@ Changes the source attributed to an existing application, so programmatically cr | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `applicationId` | string | Yes | UUID of the application whose source should change | | `sourceId` | string | No | UUID of the source to attribute the application to, as returned by List Sources. Omit only when unsetSource is true. | @@ -240,7 +237,6 @@ Moves an application to a different interview stage. Requires an archive reason | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `applicationId` | string | Yes | The UUID of the application to update the stage of | | `interviewStageId` | string | Yes | The UUID of the interview stage to move the application to | @@ -301,7 +297,6 @@ Creates a new application for a candidate on a job. Optionally specify interview | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | The UUID of the candidate to consider for the job | | `jobId` | string | Yes | The UUID of the job to consider the candidate for | @@ -366,7 +361,6 @@ Creates a new candidate record in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `name` | string | Yes | The candidate full name | | `email` | string | No | Primary email address for the candidate | @@ -434,7 +428,6 @@ Creates a note on a candidate in Ashby. Supports plain text and HTML content (bo | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | The UUID of the candidate to add the note to | | `note` | string | Yes | The note content. If noteType is text/html, supports: <b>, <i>, <u>, <a>, <ul>, <ol>, <li>, <code>, <pre> | @@ -469,7 +462,6 @@ Permanently deletes an application in Ashby. Requires the candidatesDelete permi | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `applicationId` | string | Yes | UUID of the application to delete | @@ -491,7 +483,6 @@ Retrieves full details about a single application by its ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `applicationId` | string | No | The UUID of the application to fetch | | `submittedFormInstanceId` | string | No | Submitted application-form instance UUID to use instead of applicationId | | `expand` | json | No | Ashby-supported application expansions to include | @@ -550,7 +541,6 @@ Retrieves full details about a single candidate by their ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `candidateId` | string | No | The UUID of the candidate to fetch | | `externalMappingId` | string | No | External mapping ID to use instead of the Ashby candidate UUID | @@ -608,7 +598,6 @@ Retrieves full details about a single job by its ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `jobId` | string | Yes | The UUID of the job to fetch | | `includeUnpublishedJobPostingIds` | boolean | No | Include IDs for unpublished job postings on this job | @@ -666,7 +655,6 @@ Retrieves full details about a single job posting by its ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `jobPostingId` | string | Yes | The UUID of the job posting to fetch | | `jobBoardId` | string | No | Optional job board UUID. If omitted, returns posting for the external job board. | | `expandJob` | boolean | No | Whether to expand and include the related job object in the response | @@ -742,7 +730,6 @@ Retrieves full details about a single offer by its ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `offerId` | string | Yes | The UUID of the offer to fetch | | `excludeFormDefinition` | boolean | No | Omit the offer form definition from the response | @@ -800,7 +787,6 @@ Retrieves one Ashby headcount opening by UUID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `openingId` | string | Yes | Opening UUID | #### Output @@ -857,7 +843,6 @@ Lists submitted interview feedback, optionally for one application, with paginat | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `applicationId` | string | No | Application UUID | | `cursor` | string | No | Pagination cursor | | `perPage` | number | No | Results per page \(1-100\) | @@ -894,7 +879,6 @@ Lists the full stage history and allowed actions for an application. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `applicationId` | string | Yes | Application UUID | | `cursor` | string | No | Pagination cursor | | `perPage` | number | No | Results per page \(1-100\) | @@ -927,7 +911,6 @@ Lists all applications in an Ashby organization with pagination and optional fil | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default 100\) | | `status` | string | No | Application status to include: Active, Hired, Archived, or Lead | @@ -958,7 +941,6 @@ Lists all archive reasons configured in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `includeArchived` | boolean | No | Whether to include archived archive reasons in the response \(default false\) | #### Output @@ -983,7 +965,6 @@ Lists all candidate tags configured in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `includeArchived` | boolean | No | Whether to include archived candidate tags \(default false\) | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `syncToken` | string | No | Sync token from a previous response to fetch only changed results | @@ -1013,7 +994,6 @@ Lists all candidates in an Ashby organization with cursor-based pagination. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default 100\) | | `createdAfter` | string | No | Only return candidates created after this ISO 8601 timestamp \(e.g. 2024-01-01T00:00:00Z\) | @@ -1041,7 +1021,6 @@ Lists all custom field definitions configured in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default and max 100\) | | `syncToken` | string | No | Opaque token from a prior sync to fetch only items changed since then | @@ -1079,7 +1058,6 @@ Lists all departments in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default and max 100\) | | `syncToken` | string | No | Opaque token from a prior sync to fetch only items changed since then | @@ -1114,7 +1092,6 @@ Lists interview schedules in Ashby, optionally filtered by application or interv | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `applicationId` | string | No | The UUID of the application to list interview schedules for | | `interviewStageId` | string | No | The UUID of the interview stage to list interview schedules for | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | @@ -1171,7 +1148,6 @@ Lists Ashby interview plans, including optional archived plans and incremental c | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `includeArchived` | boolean | No | Include archived interview plans | | `cursor` | string | No | Pagination cursor | | `perPage` | number | No | Results per page \(1-100\) | @@ -1203,7 +1179,6 @@ Lists the ordered stages in an Ashby interview plan. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `interviewPlanId` | string | Yes | Interview plan UUID | #### Output @@ -1230,7 +1205,6 @@ Lists all job postings in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `location` | string | No | Filter by location name \(case sensitive\) | | `department` | string | No | Filter by department name \(case sensitive\) | | `listedOnly` | boolean | No | When true, only returns listed \(publicly visible\) job postings \(default false\) | @@ -1275,7 +1249,6 @@ Lists all jobs in an Ashby organization. By default returns Open, Closed, and Ar | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default and max 100\). Ashby silently caps larger values rather than erroring. | | `syncToken` | string | No | Opaque token from a prior sync to fetch only jobs changed since then. Ashby only returns a new syncToken on the last page, so drain moreDataAvailable/nextCursor before persisting it. | @@ -1308,7 +1281,6 @@ Lists all locations configured in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default and max 100\) | | `syncToken` | string | No | Opaque token from a prior sync to fetch only items changed since then | @@ -1351,7 +1323,6 @@ Lists all notes on a candidate with pagination support. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `candidateId` | string | Yes | The UUID of the candidate to list notes for | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(1-100\) | @@ -1385,7 +1356,6 @@ Lists all offers with their latest version in an Ashby organization. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page | | `createdAfter` | string | No | Only return offers created after this ISO 8601 timestamp \(e.g. 2024-01-01T00:00:00Z\) | @@ -1416,7 +1386,6 @@ Lists all openings in Ashby with pagination. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default 100\) | | `createdAfter` | string | No | Only return openings created after this ISO 8601 timestamp \(e.g. 2024-01-01T00:00:00Z\) | @@ -1442,7 +1411,6 @@ Lists all candidate sources configured in Ashby. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `includeArchived` | boolean | No | When true, includes archived sources in results \(default false\) | #### Output @@ -1470,7 +1438,6 @@ Lists all users in Ashby with pagination. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value | | `perPage` | number | No | Number of results per page \(default 100\) | | `includeDeactivated` | boolean | No | When true, includes deactivated users in results \(default false\) | @@ -1497,7 +1464,6 @@ Removes a tag from a candidate in Ashby and returns the updated candidate. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | The UUID of the candidate to remove the tag from | | `tagId` | string | Yes | The UUID of the tag to remove | @@ -1556,7 +1522,6 @@ Searches for candidates by name and/or email with AND logic. Results are limited | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `name` | string | No | Candidate name to search for \(combined with email using AND logic\) | | `email` | string | No | Candidate email to search for \(combined with name using AND logic\) | | `limit` | number | No | Maximum matches to return \(1-100\) | @@ -1579,7 +1544,6 @@ Searches Ashby jobs by title and/or requisition ID. Provide at least one of thes | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `title` | string | No | Job title search text | | `requisitionId` | string | No | Custom requisition ID | | `limit` | number | No | Maximum matches \(1-100\) | @@ -1603,7 +1567,6 @@ Searches Ashby headcount openings by human-readable identifier. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `identifier` | string | Yes | Opening identifier | #### Output @@ -1660,7 +1623,6 @@ Searches Ashby users by exact email address. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `email` | string | Yes | User email address | #### Output @@ -1681,7 +1643,6 @@ Sets the value of a single custom field on an Ashby Application, Candidate, Job, | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `objectId` | string | Yes | UUID of the object to set the field on \(application, candidate, job, or opening\) | | `objectType` | string | Yes | Type of the object: Application, Candidate, Job, or Opening | @@ -1706,7 +1667,6 @@ Sets several custom field values on one Ashby Application, Candidate, Job, or Op | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `objectId` | string | Yes | UUID of the object to set the fields on \(application, candidate, job, or opening\) | | `objectType` | string | Yes | Type of the object: Application, Candidate, Job, or Opening | @@ -1730,7 +1690,6 @@ Updates an existing candidate record in Ashby. Only provided fields are changed. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `candidateId` | string | Yes | The UUID of the candidate to update | | `name` | string | No | Updated full name, or null | @@ -1803,7 +1762,6 @@ Transfers an application to another job, interview plan, and stage. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls | | `applicationId` | string | Yes | Application UUID | | `jobId` | string | Yes | Destination job UUID | @@ -1865,7 +1823,6 @@ Securely uploads a file and attaches it to an Ashby candidate. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `candidateId` | string | Yes | Candidate UUID | | `file` | file | Yes | Stored file to attach to the candidate | | `fileName` | string | No | Optional filename override | @@ -1925,7 +1882,6 @@ Securely uploads a resume and sets it as the Ashby candidate resume. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Ashby API Key | | `candidateId` | string | Yes | Candidate UUID | | `file` | file | Yes | Stored resume file | | `fileName` | string | No | Optional filename override | diff --git a/apps/docs/content/docs/integrations/aws-credentials.mdx b/apps/docs/content/docs/integrations/aws-credentials.mdx index 7643a969ef5..8282645bfac 100644 --- a/apps/docs/content/docs/integrations/aws-credentials.mdx +++ b/apps/docs/content/docs/integrations/aws-credentials.mdx @@ -58,8 +58,21 @@ To rotate a key, create the new key in AWS, reconnect the credential with it, co Keyless roles need two things on a self-hosted deployment: - `WORKLOAD_IDENTITY_SIGNING_KEY`: a PKCS#8 PEM RSA private key that signs Sim's tokens. -- An issuer AWS can reach over the public internet. It defaults to `/oidc/workload`; set `WORKLOAD_IDENTITY_ISSUER` to pin it. Trust policies reference this URL, so never change it once roles trust it. +- An issuer AWS can reach over the public internet: an `https` URL on the default port, served by a certificate from a public CA. It defaults to `/oidc/workload`; set `WORKLOAD_IDENTITY_ISSUER` to pin it. Trust policies reference this URL, so never change it once roles trust it. Sim keeps keyless off when the issuer isn't `https` or names a port, query, or fragment, because AWS can't use it. -To rotate the signing key, move the old key to `WORKLOAD_IDENTITY_PREVIOUS_KEY` and set a new `WORKLOAD_IDENTITY_SIGNING_KEY`. Both keys stay published until you remove the old one. +Generate a key with `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048`. + +To rotate the signing key without failed runs: + +1. Set the new key as `WORKLOAD_IDENTITY_PREVIOUS_KEY` and redeploy (including any background workers that run workflows). Sim publishes it but keeps signing with the current key. +2. Wait at least 5 minutes, so every cached copy of the published keys includes the new one. +3. Swap them: set the new key as `WORKLOAD_IDENTITY_SIGNING_KEY` and the old key as `WORKLOAD_IDENTITY_PREVIOUS_KEY`, and redeploy everything that runs workflows. +4. Once every instance signs with the new key, wait 5 more minutes, then remove `WORKLOAD_IDENTITY_PREVIOUS_KEY`. Without a signing key, the keyless option is unavailable and access keys work as usual. + +## Troubleshooting + +- **`InvalidIdentityToken` or "No OpenIDConnect provider found":** the IAM identity provider's URL or audience doesn't match the dialog exactly, or AWS can't reach the issuer. Open `/.well-known/openid-configuration` from outside your network; it must load over `https`. +- **"Sim could not assume this role: Not authorized to perform sts:AssumeRoleWithWebIdentity":** the role's trust policy doesn't trust this credential's subject. Paste the trust policy from the dialog again; if you chose **Issue a new subject**, update the policy to the new one. +- **"This role trusts every Sim subject":** the trust policy is missing the `sub` condition. Use the policy from the dialog. diff --git a/apps/docs/content/docs/integrations/calendly.mdx b/apps/docs/content/docs/integrations/calendly.mdx index 59b04c7b283..1b257c6091d 100644 --- a/apps/docs/content/docs/integrations/calendly.mdx +++ b/apps/docs/content/docs/integrations/calendly.mdx @@ -2,7 +2,7 @@ title: Calendly description: Manage Calendly scheduling and events integration: - type: "calendly" + type: "calendly_v2" color: "#FFFFFF" --- @@ -17,7 +17,7 @@ Webhook subscriptions require a paid plan and the permissions for their user or ## Use the block [#usage-instructions] -Manage event types, scheduled events, invitees, and webhooks. Trigger workflows based on Calendly webhook events (invitee scheduled, invitee canceled, routing form submitted). Requires Personal Access Token. +Manage event types, scheduled events, invitees, and webhooks. Trigger workflows based on Calendly webhook events (invitee scheduled, invitee canceled, routing form submitted). ## Actions @@ -31,7 +31,6 @@ Get information about the currently authenticated Calendly user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | #### Output @@ -61,7 +60,6 @@ Get information about a specific Calendly user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `userUuid` | string | Yes | User UUID. Format: UUID \(e.g., "abc123-def456"\), full URI \(e.g., "https://api.calendly.com/users/abc123-def456"), or the constant "me" for the authenticated user | #### Output @@ -95,7 +93,6 @@ Retrieve a list of all event types for a user or organization | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `user` | string | No | Return only event types that belong to this user. Format: URI \(e.g., "https://api.calendly.com/users/abc123-def456") | | `organization` | string | No | Return only event types that belong to this organization. Format: URI \(e.g., "https://api.calendly.com/organizations/abc123-def456") | | `count` | number | No | Number of results per page. Format: integer \(default: 20, max: 100\) | @@ -140,7 +137,6 @@ Get detailed information about a specific event type | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventTypeUuid` | string | Yes | Event type UUID. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/event_types/abc123-def456") | #### Output @@ -181,7 +177,6 @@ Retrieve bookable time slots for an event type within a date range | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventTypeUri` | string | Yes | Event type to check. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/event_types/abc123-def456") | | `startTime` | string | Yes | Start of the availability range. Cannot be in the past. Format: ISO 8601 \(e.g., "2024-01-01T00:00:00Z"\) | | `endTime` | string | Yes | End of the availability range. Must be in the future and no more than 31 days after the start. Format: ISO 8601 \(e.g., "2024-01-15T00:00:00Z"\) | @@ -208,7 +203,6 @@ Retrieve a list of scheduled events for a user or organization | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `user` | string | No | Return events that belong to this user. Either "user" or "organization" must be provided. Format: URI \(e.g., "https://api.calendly.com/users/abc123-def456") | | `organization` | string | No | Return events that belong to this organization. Either "user" or "organization" must be provided. Format: URI \(e.g., "https://api.calendly.com/organizations/abc123-def456") | | `invitee_email` | string | No | Return events where invitee has this email | @@ -259,7 +253,6 @@ Get detailed information about a specific scheduled event | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventUuid` | string | Yes | Scheduled event UUID. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/scheduled_events/abc123-def456") | #### Output @@ -304,7 +297,6 @@ Retrieve a list of invitees for a scheduled event | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventUuid` | string | Yes | Scheduled event UUID. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/scheduled_events/abc123-def456") | | `count` | number | No | Number of results per page. Format: integer \(default: 20, max: 100\) | | `email` | string | No | Filter invitees by email address | @@ -353,7 +345,6 @@ Retrieve a single invitee of a scheduled event, including their intake answers | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventUuid` | string | Yes | Scheduled event UUID. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/scheduled_events/abc123-def456") | | `inviteeUuid` | string | Yes | Invitee UUID. Format: UUID \(e.g., "abc123-def456"\) or full invitee URI \(e.g., "https://api.calendly.com/scheduled_events/abc123/invitees/def456") | @@ -416,7 +407,6 @@ Book a meeting by creating an invitee on an event type at a chosen time. Require | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventTypeUri` | string | Yes | Event type to book. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/event_types/abc123-def456") | | `startTime` | string | Yes | Start time of the booking in UTC. Must be an available slot. Format: ISO 8601 \(e.g., "2024-01-15T09:00:00Z"\) | | `inviteeEmail` | string | Yes | Email address of the invitee being booked | @@ -463,7 +453,6 @@ Cancel a scheduled event | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventUuid` | string | Yes | Scheduled event UUID to cancel. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/scheduled_events/abc123-def456") | | `reason` | string | No | Reason for cancellation \(will be sent to invitees\) | @@ -489,7 +478,6 @@ Mark an invitee of a scheduled event as a no-show | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `inviteeUri` | string | Yes | Full invitee URI to mark as a no-show. Format: URI \(e.g., "https://api.calendly.com/scheduled_events/abc123/invitees/def456") | #### Output @@ -513,7 +501,6 @@ Remove the no-show status from an invitee | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `noShowUuid` | string | Yes | No-show UUID to remove. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/invitee_no_shows/abc123-def456") | #### Output @@ -535,7 +522,6 @@ Create a single-use scheduling link for an event type | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `eventTypeUri` | string | Yes | Event type the link books. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/event_types/abc123-def456") | #### Output @@ -559,7 +545,6 @@ Retrieve a user's internal and external busy times within a date range, based on | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `user` | string | Yes | User whose busy times are returned. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/users/abc123-def456") | | `startTime` | string | Yes | Start of the requested range. Cannot be in the past. Format: ISO 8601 \(e.g., "2024-01-01T00:00:00Z"\) | | `endTime` | string | Yes | End of the requested range. Must be after the start and no more than 7 days later. Format: ISO 8601 \(e.g., "2024-01-07T00:00:00Z"\) | @@ -589,7 +574,6 @@ Retrieve a user's availability schedules, working hours, and date overrides | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `user` | string | Yes | User whose schedules are returned. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/users/abc123-def456") | #### Output @@ -622,7 +606,6 @@ Retrieve the members of an organization, including each member profile and role | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `organization` | string | No | Return memberships that belong to this organization. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/organizations/abc123-def456") | | `user` | string | No | Return memberships that belong to this user. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/users/abc123-def456") | | `email` | string | No | Filter memberships by member email address | @@ -671,7 +654,6 @@ Retrieve the routing forms of an organization, including their questions | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `organization` | string | Yes | Organization whose routing forms are returned. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/organizations/abc123-def456") | | `count` | number | No | Number of results per page. Format: integer \(default: 20, max: 100\) | | `pageToken` | string | No | Page token for pagination. Format: opaque string from previous response next_page_token | @@ -713,7 +695,6 @@ Retrieve the submissions of a routing form, including answers and routing result | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Calendly Personal Access Token | | `formUri` | string | Yes | Routing form whose submissions are returned. Format: UUID \(e.g., "abc123-def456"\) or full URI \(e.g., "https://api.calendly.com/routing_forms/abc123-def456") | | `count` | number | No | Number of results per page. Format: integer \(default: 20, max: 100\) | | `pageToken` | string | No | Page token for pagination. Format: opaque string from previous response next_page_token | diff --git a/apps/docs/content/docs/integrations/checkr.mdx b/apps/docs/content/docs/integrations/checkr.mdx index 5f616b42873..8fb8013af06 100644 --- a/apps/docs/content/docs/integrations/checkr.mdx +++ b/apps/docs/content/docs/integrations/checkr.mdx @@ -2,7 +2,7 @@ title: Checkr description: Run and manage background checks with Checkr integration: - type: "checkr" + type: "checkr_v2" color: "#009BB5" --- @@ -33,7 +33,6 @@ Add a tag to a report, keeping its existing tags. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | | `tag` | string | Yes | Tag to add, e.g. To Review | @@ -56,7 +55,6 @@ Continue or skip the remaining screenings on a report that is paused awaiting a | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the paused report | | `decision` | string | Yes | continue to run the remaining screenings, or skip_remaining to skip them and complete the report | @@ -78,7 +76,6 @@ Cancel an adverse action so the post-adverse action notice is not sent. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `adverseActionId` | string | Yes | ID of the adverse action to cancel | #### Output @@ -123,7 +120,6 @@ Cancel a continuous check to stop monitoring the candidate. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `continuousCheckId` | string | Yes | ID of the continuous check to cancel | #### Output @@ -153,7 +149,6 @@ Cancel a pending invitation so the candidate can no longer complete it. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `invitationId` | string | Yes | ID of the invitation to cancel | #### Output @@ -187,7 +182,6 @@ Cancel a subscription so no further recurring checks run. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `subscriptionId` | string | Yes | ID of the subscription to cancel | #### Output @@ -224,7 +218,6 @@ Complete a report now, canceling any pending or suspended screenings. Canceled s | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report to complete | #### Output @@ -300,7 +293,6 @@ Start the adverse action process on a report by sending the pre-adverse action n | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report the adverse action is based on | | `adverseItemIds` | array | Yes | IDs of the adverse items to cite, from List Adverse Items | | `postNoticeScheduledAt` | string | No | ISO 8601 time to send the post-adverse action notice; defaults to 7 days after creation | @@ -349,7 +341,6 @@ Create a candidate to screen. Only email is required when the candidate will be | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `email` | string | Yes | Candidate's email address | | `firstName` | string | No | Candidate's first name | | `middleName` | string | No | Candidate's middle name \(required to order a report unless noMiddleName is true\) | @@ -413,7 +404,6 @@ Enroll a candidate in continuous criminal or motor vehicle monitoring so new rec | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to enroll | | `continuousCheckType` | string | Yes | Type of monitoring: criminal or mvr | | `mvrEnrollmentType` | string | No | For mvr checks, the enrollment type: commercial for commercial drivers or standard | @@ -447,7 +437,6 @@ Create a geo to group candidates by work location. A geo with the same name and | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `name` | string | Yes | Name of the geo, e.g. San Francisco | | `state` | string | Yes | Two-letter US state for the geo, e.g. CA | | `city` | string | No | A major city within the state | @@ -477,7 +466,6 @@ Invite a candidate to complete a background check through the Checkr-hosted appl | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to invite | | `package` | string | Yes | Slug of the package to run, e.g. driver_pro | | `node` | string | No | custom_id of the account hierarchy node \(required for hierarchy-enabled accounts\) | @@ -515,7 +503,6 @@ Order a background check report for a candidate using a package. Use only when y | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to screen | | `package` | string | Yes | Slug of the package to run, e.g. driver_pro | | `node` | string | No | custom_id of the account hierarchy node \(required for hierarchy-enabled accounts\) | @@ -596,7 +583,6 @@ Schedule a recurring background check for a candidate, running a package every i | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to screen | | `package` | string | Yes | Slug of the package to run on each occurrence | | `startDate` | string | Yes | Date of the first check in YYYY-MM-DD format | @@ -639,7 +625,6 @@ Request removal of a candidate's personally identifiable information. Fails if P | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate whose PII should be removed | | `deletionContactEmail` | string | Yes | Email address of the person requesting the PII removal | | `deletionContactFirstName` | string | No | First name of the person requesting the PII removal | @@ -687,7 +672,6 @@ Delete a geo. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `geoId` | string | Yes | ID of the geo to delete | #### Output @@ -709,7 +693,6 @@ Retrieve the authenticated Checkr account, including whether it is authorized to | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | #### Output @@ -749,7 +732,6 @@ Retrieve an adverse action by ID, including its status and notice delivery state | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `adverseActionId` | string | Yes | ID of the adverse action | #### Output @@ -794,7 +776,6 @@ Retrieve a candidate by ID, including their report and geo IDs. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to retrieve | #### Output @@ -839,7 +820,6 @@ Retrieve a continuous check by ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `continuousCheckId` | string | Yes | ID of the continuous check | #### Output @@ -869,7 +849,6 @@ Retrieve a report or candidate document by ID, including a download link valid f | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `documentId` | string | Yes | ID of the document | #### Output @@ -898,7 +877,6 @@ Retrieve a geo by ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `geoId` | string | Yes | ID of the geo | #### Output @@ -926,7 +904,6 @@ Retrieve an invitation by ID, including its status and invitation URL. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `invitationId` | string | Yes | ID of the invitation to retrieve | | `includeDeleted` | boolean | No | Whether to return the invitation even if it was canceled | @@ -961,7 +938,6 @@ Retrieve an account hierarchy node by its custom ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `nodeCustomId` | string | Yes | Custom ID of the node | | `includePackages` | boolean | No | Include the package slugs available to the node | @@ -988,7 +964,6 @@ Retrieve a package by ID, including its slug, price, and screenings. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `packageId` | string | Yes | ID of the package | #### Output @@ -1020,7 +995,6 @@ Retrieve a program by ID, including its package and geo IDs. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `programId` | string | Yes | ID of the program | #### Output @@ -1047,7 +1021,6 @@ Retrieve a report by ID, including its status, result, adjudication, and the IDs | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report to retrieve | #### Output @@ -1123,7 +1096,6 @@ Retrieve the estimated completion date for a report. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | #### Output @@ -1145,7 +1117,6 @@ Retrieve checkpoint progress for an Enterprise Progressive report, including whi | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the progressive report | #### Output @@ -1181,7 +1152,6 @@ List the tags on a report. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | #### Output @@ -1203,7 +1173,6 @@ Retrieve the results of one screening in a report, such as an SSN trace, county | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `screeningType` | string | Yes | Screening type, one of: ssn_trace, sex_offender_search, global_watchlist_search, national_criminal_search, county_criminal_search, state_criminal_search, federal_criminal_search, federal_district_criminal_search, federal_civil_search, federal_district_civil_search, motor_vehicle_report, drug_alcohol_clearinghouse_search, fmcsa_pre_employment_screening_program_search, education_verification, employment_verification, personal_reference_verification, professional_reference_verification, professional_license_verification, social_media_screening, facis_search, identity_data_evaluation, international_adverse_media_search, international_criminal_search, international_education_verification, international_employment_verification, international_global_watchlist_search, international_identity_document_validation, international_motor_vehicle_report | | `screeningId` | string | Yes | ID of the screening, taken from the matching report field | @@ -1239,7 +1208,6 @@ Retrieve a subscription by ID, including its schedule and next occurrence. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `subscriptionId` | string | Yes | ID of the subscription | #### Output @@ -1276,7 +1244,6 @@ Retrieve a verification by ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `verificationId` | string | Yes | ID of the verification | #### Output @@ -1305,7 +1272,6 @@ List the adverse actions for a report, optionally filtered by context. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | | `context` | string | No | Only return adverse actions with this context | @@ -1354,7 +1320,6 @@ List the adverse items on a report that can be cited in an adverse action. The r | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | #### Output @@ -1379,7 +1344,6 @@ List the Assess results for a report, including the ruleset applied and which ru | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | #### Output @@ -1405,7 +1369,6 @@ List documents a candidate provided, such as driver licenses and consent forms. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate | | `documentTypes` | array | No | Only return these document types, e.g. driver_license, consent | @@ -1436,7 +1399,6 @@ List candidates, optionally filtered by email, name, custom ID, adjudication, da | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `email` | string | No | Only return candidates with this email address | | `fullName` | string | No | Only return candidates with this full name | | `customId` | string | No | Only return candidates with this custom ID | @@ -1499,7 +1461,6 @@ List the continuous checks a candidate is enrolled in. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate | #### Output @@ -1530,7 +1491,6 @@ List US counties for one or more states, with their FIPS codes. Use the county n | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `states` | string | No | Comma-separated state FIPS codes, e.g. 08,06 for Colorado and California. Leave empty for every US county | #### Output @@ -1554,7 +1514,6 @@ List geos (work locations used for compliance), optionally filtered by name or s | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `name` | string | No | Only return geos with this name | | `state` | string | No | Only return geos in this two-letter state | | `page` | number | No | Page number to retrieve \(starts at 1\) | @@ -1588,7 +1547,6 @@ List invitations, optionally filtered by candidate or status. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | No | Only return invitations for this candidate ID | | `status` | string | No | Only return invitations with this status \(pending, completed, expired\) | | `page` | number | No | Page number to retrieve \(starts at 1\) | @@ -1628,7 +1586,6 @@ List the nodes in the account hierarchy. Hierarchy-enabled accounts need a node | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `includePackages` | boolean | No | Include the package slugs available to each node | | `orderBy` | string | No | Sort field: custom_id or created_at \(default created_at\) | | `order` | string | No | Sort direction: asc or desc \(default asc\) | @@ -1661,7 +1618,6 @@ List the background check packages on the account, with their slugs, prices, and | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `page` | number | No | Page number to retrieve \(starts at 1\) | | `perPage` | number | No | Number of records per page, from 0 to 100 \(default 25\) | @@ -1697,7 +1653,6 @@ List programs, which group packages and geos, optionally filtered by name. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `name` | string | No | Only return programs with this name | | `page` | number | No | Page number to retrieve \(starts at 1\) | | `perPage` | number | No | Number of records per page, from 0 to 100 \(default 25\) | @@ -1729,7 +1684,6 @@ List the address history found for a report. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | | `page` | number | No | Page number to retrieve \(starts at 1\) | | `perPage` | number | No | Number of records per page, from 0 to 100 \(default 25\) | @@ -1758,7 +1712,6 @@ List recurring check subscriptions, optionally filtered by candidate, status, or | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | No | Only return subscriptions for this candidate ID | | `status` | string | No | Only return subscriptions with this status \(active, inactive\) | | `createdAfter` | string | No | Only return subscriptions created after this date | @@ -1803,7 +1756,6 @@ List the users on the Checkr account and their roles. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `page` | number | No | Page number to retrieve \(starts at 1\) | | `perPage` | number | No | Number of records per page, from 0 to 100 \(default 25\) | @@ -1833,7 +1785,6 @@ List the verifications on a report, such as requests for the candidate to confir | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | #### Output @@ -1863,7 +1814,6 @@ Remove a tag from a report. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | | `tag` | string | Yes | Tag to remove | @@ -1886,7 +1836,6 @@ Replace all tags on a report with the given list. Pass [] to remove every tag. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report | | `tags` | array | Yes | Complete list of tags; an empty list removes every tag | @@ -1909,7 +1858,6 @@ Update a candidate. Once a report is ordered only empty fields can change, excep | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `candidateId` | string | Yes | ID of the candidate to update | | `email` | string | No | Candidate's email address | | `firstName` | string | No | Candidate's first name | @@ -1973,7 +1921,6 @@ Change the node or work locations of a continuous check. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `continuousCheckId` | string | Yes | ID of the continuous check to update | | `node` | string | No | custom_id of the account hierarchy node \(required for hierarchy-enabled accounts\) | | `workLocations` | array | No | Work locations \(required for hierarchy-enabled accounts\), e.g. \[\{"country":"US","state":"CA","city":"San Francisco"\}\] | @@ -2005,7 +1952,6 @@ Add a city to a geo. A geo’s city can only be set once. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `geoId` | string | Yes | ID of the geo | | `city` | string | Yes | City to set on the geo | @@ -2034,7 +1980,6 @@ Upgrade a report to a different package or mark it as engaged. Provide a package | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `reportId` | string | Yes | ID of the report to update | | `package` | string | No | Slug of the package to upgrade the report to | | `adjudication` | string | No | Adjudication to set; Checkr accepts only "engaged" | @@ -2112,7 +2057,6 @@ Change the package, schedule, node, or work locations of a subscription. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Checkr production secret API key | | `subscriptionId` | string | Yes | ID of the subscription to update | | `package` | string | No | Slug of the package to run on each occurrence | | `startDate` | string | No | Date of the first check in YYYY-MM-DD format | diff --git a/apps/docs/content/docs/integrations/databricks-credentials.mdx b/apps/docs/content/docs/integrations/databricks-credentials.mdx index c6b7b318fe6..65ab057d36e 100644 --- a/apps/docs/content/docs/integrations/databricks-credentials.mdx +++ b/apps/docs/content/docs/integrations/databricks-credentials.mdx @@ -29,10 +29,10 @@ Every option resolves on Sim's servers at run time; tokens never appear in block In Sim, add a **Databricks connection**, choose **Service principal (keyless, via Sim OIDC)**, and enter the workspace host and the service principal's **application ID**. - A Databricks account admin runs the **Federation policy** command from the dialog with the service principal's numeric ID. The policy trusts Sim's issuer, the Sim audience, and this connection's subject only. + A Databricks account admin runs the **Federation policy** command from the dialog with the service principal's numeric ID (find it with `databricks account service-principals list --filter 'applicationId eq ""'`, or on the service principal's page in the account console). The policy trusts Sim's issuer, the Sim audience, and this connection's subject only. - Make sure the service principal can access the workspace and the resources your workflows use, then save. Sim verifies the connection by exchanging a token and reading the service principal's identity. + Make sure the service principal can access the workspace and the resources your workflows use, then save. Sim verifies the connection by exchanging a token and reading the service principal's identity, and refuses a policy that would also trust a different subject, since every Sim workspace shares the issuer and audience. @@ -40,4 +40,9 @@ Reconnecting a keyless connection keeps its subject by default, so the federatio ## Self-hosted deployments -Keyless connections need `WORKLOAD_IDENTITY_SIGNING_KEY` set and an issuer Databricks can reach over the public internet. See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments) for the same settings. +Keyless connections need `WORKLOAD_IDENTITY_SIGNING_KEY` set and an `https` issuer Databricks can reach over the public internet. See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments) for the same settings and for rotating the signing key. + +## Troubleshooting + +- **"Databricks rejected the token request":** the federation policy's issuer, audience, or subject doesn't match the dialog, or the policy is on a different service principal than the application ID you entered. +- **"This service principal's federation policy also trusts other Sim credentials":** the policy's `subject_claim` isn't `sub`, so it matches more than this connection. Recreate it with the command from the dialog. diff --git a/apps/docs/content/docs/integrations/fathom.mdx b/apps/docs/content/docs/integrations/fathom.mdx index 2e18443faa4..a3c5703a3b7 100644 --- a/apps/docs/content/docs/integrations/fathom.mdx +++ b/apps/docs/content/docs/integrations/fathom.mdx @@ -2,7 +2,7 @@ title: Fathom description: Access meeting recordings, transcripts, and summaries integration: - type: "fathom" + type: "fathom_v2" color: "#181C1E" --- @@ -31,7 +31,6 @@ List recent meetings recorded by the user or shared to their team. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `includeSummary` | string | No | Include meeting summary \(true/false\) | | `includeTranscript` | string | No | Include meeting transcript \(true/false\) | | `includeActionItems` | string | No | Include action items \(true/false\) | @@ -129,7 +128,6 @@ List meeting types configured in your Fathom organization. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `cursor` | string | No | Pagination cursor from a previous response | #### Output @@ -154,7 +152,6 @@ Get the call summary for a specific meeting recording. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `recordingId` | string | Yes | The recording ID of the meeting | #### Output @@ -176,7 +173,6 @@ Get the full transcript for a specific meeting recording. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `recordingId` | string | Yes | The recording ID of the meeting | #### Output @@ -202,7 +198,6 @@ List team members in your Fathom organization. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `teams` | string | No | Team name to filter by | | `cursor` | string | No | Pagination cursor from a previous response | @@ -228,7 +223,6 @@ List teams in your Fathom organization. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Fathom API Key | | `cursor` | string | No | Pagination cursor from a previous response | #### Output diff --git a/apps/docs/content/docs/integrations/gitlab.mdx b/apps/docs/content/docs/integrations/gitlab.mdx index 7f88acc2e1b..9256c2ac0e5 100644 --- a/apps/docs/content/docs/integrations/gitlab.mdx +++ b/apps/docs/content/docs/integrations/gitlab.mdx @@ -2,7 +2,7 @@ title: GitLab description: Interact with GitLab projects, issues, merge requests, and pipelines integration: - type: "gitlab" + type: "gitlab_v2" color: "#FFFFFF" --- @@ -29,7 +29,6 @@ List GitLab projects accessible to the authenticated user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `owned` | boolean | No | Limit to projects owned by the current user | | `membership` | boolean | No | Limit to projects the current user is a member of | | `search` | string | No | Search projects by name | @@ -58,7 +57,6 @@ Get details of a specific GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) \(e.g., "namespace/project"\) | #### Output @@ -79,7 +77,6 @@ List GitLab groups accessible to the authenticated user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `owned` | boolean | No | Limit to groups owned by the current user | | `search` | string | No | Search groups by name or path | | `topLevelOnly` | boolean | No | Limit to top-level groups, excluding subgroups | @@ -110,7 +107,6 @@ Get details of a specific GitLab group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `groupId` | string | Yes | Group ID or path \(e.g. mygroup or parent/subgroup\) | #### Output @@ -131,7 +127,6 @@ List a user's project and group memberships. Requires an administrator access to | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | string | Yes | The ID of the user whose memberships to list | | `membershipType` | string | No | Filter by source: 'Project' or 'Namespace' \(group\). Omit for all memberships. | | `perPage` | number | No | Number of results per page \(default 20, max 100\) | @@ -156,7 +151,6 @@ List issues in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `state` | string | No | Filter by state \(opened, closed, all\) | | `labels` | string | No | Comma-separated list of label names | @@ -187,7 +181,6 @@ Get details of a specific GitLab issue | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `issueIid` | number | Yes | Issue number within the project \(the # shown in GitLab UI\) | @@ -209,7 +202,6 @@ Create a new issue in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `title` | string | Yes | Issue title | | `description` | string | No | Issue description \(Markdown supported\) | @@ -237,7 +229,6 @@ Update an existing issue in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `issueIid` | number | Yes | Issue internal ID \(IID\) | | `title` | string | No | New issue title | @@ -267,7 +258,6 @@ Delete an issue from a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `issueIid` | number | Yes | Issue internal ID \(IID\) | @@ -289,7 +279,6 @@ Add a comment to a GitLab issue | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `issueIid` | number | Yes | Issue internal ID \(IID\) | | `body` | string | Yes | Comment body \(Markdown supported\) | @@ -313,7 +302,6 @@ List merge requests in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `state` | string | No | Filter by state \(opened, closed, locked, merged, all\) | | `labels` | string | No | Comma-separated list of label names | @@ -343,7 +331,6 @@ Get details of a specific GitLab merge request | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | @@ -365,7 +352,6 @@ Create a new merge request in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `sourceBranch` | string | Yes | Source branch name | | `targetBranch` | string | Yes | Target branch name | @@ -396,7 +382,6 @@ Update an existing merge request in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | | `title` | string | No | New merge request title | @@ -428,7 +413,6 @@ Merge a merge request in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | | `mergeCommitMessage` | string | No | Custom merge commit message | @@ -455,7 +439,6 @@ Add a comment to a GitLab merge request | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | | `body` | string | Yes | Comment body \(Markdown supported\) | @@ -479,7 +462,6 @@ List pipelines in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `ref` | string | No | Filter by ref \(branch or tag\) | | `status` | string | No | Filter by status \(created, waiting_for_resource, preparing, pending, running, success, failed, canceling, canceled, skipped, manual, scheduled, waiting_for_callback\) | @@ -507,7 +489,6 @@ Get details of a specific GitLab pipeline | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `pipelineId` | number | Yes | Pipeline ID | @@ -529,7 +510,6 @@ Trigger a new pipeline in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `ref` | string | Yes | Branch or tag to run the pipeline on | | `variables` | array | No | Array of variables for the pipeline \(each with key, value, and optional variable_type\) | @@ -553,7 +533,6 @@ Retry a failed GitLab pipeline | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `pipelineId` | number | Yes | Pipeline ID | @@ -575,7 +554,6 @@ Cancel a running GitLab pipeline | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `pipelineId` | number | Yes | Pipeline ID | @@ -597,7 +575,6 @@ List files and directories in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `path` | string | No | Path inside the repository to list | | `ref` | string | No | Branch, tag, or commit SHA to list from | @@ -624,7 +601,6 @@ Get the contents of a file from a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `filePath` | string | Yes | Path to the file in the repository | | `ref` | string | Yes | Branch, tag, or commit SHA | @@ -654,7 +630,6 @@ Create a new file in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `filePath` | string | Yes | Path to the file in the repository | | `branch` | string | Yes | Branch to commit the new file to | @@ -684,7 +659,6 @@ Update an existing file in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `filePath` | string | Yes | Path to the file in the repository | | `branch` | string | Yes | Branch to commit the update to | @@ -715,7 +689,6 @@ Create a new branch in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `branch` | string | Yes | Name of the new branch | | `ref` | string | Yes | Source branch/tag/SHA | @@ -741,7 +714,6 @@ Delete a branch from a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `branch` | string | Yes | Name of the branch to delete | @@ -763,7 +735,6 @@ Compare two branches, tags, or commits in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `from` | string | Yes | Commit SHA or branch/tag name to compare from | | `to` | string | Yes | Commit SHA or branch/tag name to compare to | @@ -794,7 +765,6 @@ List branches in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `search` | string | No | Filter branches by name | | `perPage` | number | No | Number of results per page \(default 20, max 100\) | @@ -819,7 +789,6 @@ List commits in a GitLab project repository | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `refName` | string | No | Branch, tag, or revision range to list commits from | | `since` | string | No | Only commits after this ISO 8601 date | @@ -848,7 +817,6 @@ Get the file changes (diffs) of a GitLab merge request | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | @@ -873,7 +841,6 @@ Approve a GitLab merge request | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) | | `sha` | string | No | HEAD SHA of the merge request to approve | @@ -898,7 +865,6 @@ List jobs for a GitLab pipeline | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `pipelineId` | number | Yes | Pipeline ID | | `scope` | string | No | Filter jobs by scope \(e.g. created, running, success, failed\) | @@ -925,7 +891,6 @@ Get the log (trace) of a GitLab job | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `jobId` | number | Yes | Job ID | @@ -948,7 +913,6 @@ Trigger (play) a manual GitLab job | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `jobId` | number | Yes | Job ID | | `jobVariables` | array | No | Variables for the manual job \(array of objects with key and value\) | @@ -974,7 +938,6 @@ List releases in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `orderBy` | string | No | Order by field \(released_at, created_at\) | | `sort` | string | No | Sort direction \(asc, desc\) | @@ -1000,7 +963,6 @@ Create a new release in a GitLab project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `projectId` | string | Yes | Project ID or path \(e.g. mygroup/myproject\) | | `tagName` | string | Yes | The Git tag for the release | | `name` | string | No | The release name | @@ -1029,7 +991,6 @@ List members of a GitLab project or group. Includes members inherited from ances | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `directOnly` | boolean | No | When true, returns only direct members. Defaults to false, which also returns members inherited from ancestor groups. | @@ -1059,7 +1020,6 @@ Add an existing GitLab user to a project or group at a given access level | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `userId` | number | No | The ID of the user to add. Provide either userId or username. | @@ -1087,7 +1047,6 @@ Update a member's access level in a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `userId` | number | Yes | The ID of the member to update | @@ -1113,7 +1072,6 @@ Remove a member from a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `userId` | number | Yes | The ID of the member to remove | @@ -1138,7 +1096,6 @@ Invite a person to a GitLab project or group by email address | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `email` | string | Yes | Email address to invite \(comma-separated for multiple\) | @@ -1166,7 +1123,6 @@ List pending email invitations for a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `query` | string | No | Filter invitations by invited email | @@ -1192,7 +1148,6 @@ Update a pending invitation to a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `email` | string | Yes | Email address of the invitation to update | @@ -1217,7 +1172,6 @@ Revoke a pending email invitation to a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `email` | string | Yes | Email address of the invitation to revoke | @@ -1240,7 +1194,6 @@ List pending access requests for a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `perPage` | number | No | Number of results per page \(default 20, max 100\) | @@ -1265,7 +1218,6 @@ Approve a pending access request for a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `userId` | number | Yes | The user ID of the access requester | @@ -1289,7 +1241,6 @@ Deny (delete) a pending access request for a GitLab project or group | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `resourceType` | string | Yes | Whether the resource is a 'project' or a 'group' | | `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) | | `userId` | number | Yes | The user ID of the access requester | @@ -1312,7 +1263,6 @@ List SAML group links for a GitLab group. Use this to detect whether a group is | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `groupId` | string | Yes | Group ID or path \(e.g. my-org/my-group\) | #### Output @@ -1334,7 +1284,6 @@ Search for GitLab users by name, username, or email. Email matches must be exact | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `search` | string | Yes | Name, username, or email to search for | | `perPage` | number | No | Number of results per page \(default 20, max 100\) | | `page` | number | No | Page number for pagination | @@ -1358,7 +1307,6 @@ Create a new GitLab user. Requires an administrator token with admin_mode on the | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `email` | string | Yes | The user's email address | | `username` | string | Yes | The user's username | | `name` | string | Yes | The user's display name | @@ -1386,7 +1334,6 @@ Modify an existing GitLab user. Requires an administrator token with admin_mode | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to modify | | `email` | string | No | The user's new email address \(GitLab only allows changing to one of the user's existing verified secondary emails\) | | `username` | string | No | The user's new username | @@ -1411,7 +1358,6 @@ Delete a GitLab user. Requires an administrator token with admin_mode on the ins | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to delete | | `hardDelete` | boolean | No | When true, contributions, personal projects, AND groups owned solely by this user are deleted rather than moved to a Ghost User | @@ -1433,7 +1379,6 @@ Block a GitLab user, preventing them from signing in or accessing the instance | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1511,7 +1456,6 @@ Unblock a previously blocked GitLab user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1589,7 +1533,6 @@ Deactivate a dormant GitLab user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1667,7 +1610,6 @@ Reactivate a deactivated GitLab user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1745,7 +1687,6 @@ Ban a GitLab user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1823,7 +1764,6 @@ Unban a previously banned GitLab user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1901,7 +1841,6 @@ Approve a GitLab user whose signup is pending administrator approval | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -1979,7 +1918,6 @@ Reject a GitLab user whose signup is pending administrator approval | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user to act on | #### Output @@ -2057,7 +1995,6 @@ Delete a user's authentication identity (e.g. SAML or LDAP). Requires an adminis | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `userId` | number | Yes | The ID of the user | | `provider` | string | Yes | The external identity provider name \(e.g. saml, ldapmain\) | @@ -2079,7 +2016,6 @@ Add a SAML group link that maps an identity-provider group to a GitLab group at | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `groupId` | string | Yes | Group ID or path \(e.g. my-org/my-group\) | | `samlGroupName` | string | Yes | The name of the SAML group as sent by the identity provider | | `accessLevel` | number | Yes | Access level granted to members of the SAML group: 10 \(Guest\), 15 \(Planner\), 20 \(Reporter\), 25 \(Security Manager\), 30 \(Developer\), 40 \(Maintainer\), 50 \(Owner\) | @@ -2104,7 +2040,6 @@ Delete a SAML group link from a GitLab group (GitLab Premium/Ultimate) | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. | | `groupId` | string | Yes | Group ID or path \(e.g. my-org/my-group\) | | `samlGroupName` | string | Yes | The name of the SAML group link to delete | | `provider` | string | No | Provider name of the link to delete. Required when multiple links share the same SAML group name. | diff --git a/apps/docs/content/docs/integrations/grain.mdx b/apps/docs/content/docs/integrations/grain.mdx index 99104201be1..19f1826bb21 100644 --- a/apps/docs/content/docs/integrations/grain.mdx +++ b/apps/docs/content/docs/integrations/grain.mdx @@ -2,7 +2,7 @@ title: Grain description: Access meeting recordings, transcripts, and AI summaries integration: - type: "grain_v2" + type: "grain_v3" color: "#F6FAF9" --- @@ -31,7 +31,6 @@ List recordings from Grain with optional filters and pagination | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal Access Token\) | | `cursor` | string | No | Pagination cursor for next page \(returned from previous response\) | | `beforeDatetime` | string | No | Only recordings before this ISO8601 timestamp \(e.g., "2024-01-15T00:00:00Z"\) | | `afterDatetime` | string | No | Only recordings after this ISO8601 timestamp \(e.g., "2024-01-01T00:00:00Z"\) | @@ -75,7 +74,6 @@ Get details of a single recording by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal Access Token\) | | `recordingId` | string | Yes | The recording UUID \(e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890"\) | | `includeHighlights` | boolean | No | Include highlights/clips | | `includeParticipants` | boolean | No | Include participant list | @@ -119,7 +117,6 @@ Get the full transcript of a recording | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal Access Token\) | | `recordingId` | string | Yes | The recording UUID \(e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890"\) | #### Output @@ -145,7 +142,6 @@ List all teams in the workspace | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal Access Token\) | #### Output @@ -167,7 +163,6 @@ List all meeting types in the workspace | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal Access Token\) | #### Output @@ -190,7 +185,6 @@ Create a webhook for a specific Grain event type (v2 API) | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal or Workspace Access Token\) | | `hookUrl` | string | Yes | Webhook endpoint URL. Grain performs a reachability test on creation — the endpoint must respond 2xx. | | `hookType` | string | Yes | Event type the hook subscribes to. One of: recording_added, recording_updated, recording_deleted, highlight_added, highlight_updated, highlight_deleted, story_added, story_updated, story_deleted, upload_status | | `include` | json | No | Optional include object controlling payload richness. For recording hooks: \{"participants": true, "highlights": true, "ai_summary": true\}. For highlight hooks: \{"transcript": true, "speakers": true\}. | @@ -218,7 +212,6 @@ List webhooks for the account (v2 API) | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal or Workspace Access Token\) | | `hookType` | string | No | Only return hooks with this event type. One of: recording_added, recording_updated, recording_deleted, highlight_added, highlight_updated, highlight_deleted, story_added, story_updated, story_deleted, upload_status | | `state` | string | No | Only return hooks that are "enabled" or "disabled" | @@ -246,7 +239,6 @@ Delete a webhook by ID (v2 API) | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Grain API key \(Personal or Workspace Access Token\) | | `hookId` | string | Yes | The hook UUID to delete \(e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890"\) | #### Output diff --git a/apps/docs/content/docs/integrations/granola.mdx b/apps/docs/content/docs/integrations/granola.mdx index 483206f7571..70a629b17ca 100644 --- a/apps/docs/content/docs/integrations/granola.mdx +++ b/apps/docs/content/docs/integrations/granola.mdx @@ -2,7 +2,7 @@ title: Granola description: Access meeting notes, transcripts, and audit events from Granola integration: - type: "granola" + type: "granola_v2" color: "#B2C147" --- @@ -31,7 +31,6 @@ Lists meeting notes from Granola with optional date filters and pagination. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `createdBefore` | string | No | Return notes created before this date \(ISO 8601\) | | `createdAfter` | string | No | Return notes created after this date \(ISO 8601\) | | `updatedAfter` | string | No | Return notes updated after this date \(ISO 8601\) | @@ -65,7 +64,6 @@ Retrieves a specific meeting note from Granola by ID, including summary, attende | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `noteId` | string | Yes | The note ID \(e.g., not_1d3tmYTlCICgjy\) | | `includeTranscript` | string | No | Whether to include the meeting transcript | @@ -115,7 +113,6 @@ Retrieves a meeting transcript from Granola one page at a time, including when G | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `noteId` | string | Yes | The note ID \(e.g., not_1d3tmYTlCICgjy\) | | `cursor` | string | No | Pagination cursor from a previous response | | `pageSize` | number | No | Number of transcript items per page \(1-100, default 50\) | @@ -147,7 +144,6 @@ Lists folders from Granola, sorted alphabetically, with pagination. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `cursor` | string | No | Pagination cursor from a previous response | | `pageSize` | number | No | Number of folders per page \(1-30, default 10\) | @@ -174,7 +170,6 @@ Lists workspace audit events from Granola, with optional action and date filters | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `action` | string | No | Return only events with this exact action, or actions beginning with it followed by a dot \(e.g., "workspace" matches workspace.member_added\). Lowercase. | | `occurredAfter` | string | No | Return events that occurred after this date \(ISO 8601\). Must fall within the one-year retention window. | | `occurredBefore` | string | No | Return events that occurred before this date \(ISO 8601\). Must fall within the one-year retention window. | @@ -212,7 +207,6 @@ Registers an HTTPS URL in Granola to receive note event deliveries. The signing | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `url` | string | Yes | The publicly reachable HTTPS URL to deliver events to. Private network addresses are rejected. | | `scopes` | string | Yes | Which notes to receive events for, comma-separated: personal, public. With a workspace API key pass exactly "workspace". | | `events` | string | No | Event names to subscribe to, comma-separated: note.generated, note.edited, note.access_granted. Omit to subscribe to all events. | @@ -246,7 +240,6 @@ Lists the Granola webhook endpoints the API key can manage. A personal key sees | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | #### Output @@ -276,7 +269,6 @@ Updates a Granola webhook endpoint. Each supplied field replaces its current val | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `webhookEndpointId` | string | Yes | The webhook endpoint ID \(e.g., whe_2mKr8fQxLp7Ta3\) | | `url` | string | No | New HTTPS URL to deliver events to. Omit to leave unchanged. | | `scopes` | string | No | Replacement scopes, comma-separated: personal, public. Omit to leave unchanged. A workspace-managed endpoint accepts only "workspace". | @@ -311,7 +303,6 @@ Deletes a Granola webhook endpoint by ID, stopping its event deliveries immediat | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Granola API key | | `webhookEndpointId` | string | Yes | The webhook endpoint ID \(e.g., whe_2mKr8fQxLp7Ta3\) | #### Output diff --git a/apps/docs/content/docs/integrations/lemlist.mdx b/apps/docs/content/docs/integrations/lemlist.mdx index b6477687b85..02cbb9ce0ce 100644 --- a/apps/docs/content/docs/integrations/lemlist.mdx +++ b/apps/docs/content/docs/integrations/lemlist.mdx @@ -2,7 +2,7 @@ title: Lemlist description: Manage outreach activities, leads, and send emails via Lemlist integration: - type: "lemlist" + type: "lemlist_v2" color: "#316BFF" --- @@ -31,7 +31,6 @@ Retrieves campaign activities and steps performed, including email opens, clicks | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Lemlist API key | | `type` | string | No | Filter by activity type \(e.g., emailOpened, emailClicked, emailReplied, paused\) | | `campaignId` | string | No | Filter by campaign ID \(e.g., "cam_abc123def456"\) | | `leadId` | string | No | Filter by lead ID \(e.g., "lea_abc123def456"\) | @@ -65,7 +64,6 @@ Retrieves lead information by email address or lead ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Lemlist API key | | `leadIdentifier` | string | Yes | Lead email address \(e.g., "john@example.com"\) or lead ID \(e.g., "lea_abc123def456"\) | #### Output @@ -96,7 +94,6 @@ Sends an email to a contact through the Lemlist inbox. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Lemlist API key | | `sendUserId` | string | Yes | Identifier for the user sending the message \(e.g., "usr_abc123def456"\) | | `sendUserEmail` | string | Yes | Email address of the sender \(e.g., "sales@company.com"\) | | `sendUserMailboxId` | string | Yes | Mailbox identifier for the sender \(e.g., "mbx_abc123def456"\) | diff --git a/apps/docs/content/docs/integrations/pagerduty.mdx b/apps/docs/content/docs/integrations/pagerduty.mdx index bf354e22aba..a2182e65860 100644 --- a/apps/docs/content/docs/integrations/pagerduty.mdx +++ b/apps/docs/content/docs/integrations/pagerduty.mdx @@ -2,7 +2,7 @@ title: PagerDuty description: Manage incidents and on-call schedules with PagerDuty integration: - type: "pagerduty" + type: "pagerduty_v2" color: "#06AC38" --- @@ -29,7 +29,6 @@ List incidents from PagerDuty with optional filters. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `statuses` | string | No | Comma-separated statuses to filter \(triggered, acknowledged, resolved\) | | `urgencies` | string | No | Comma-separated urgencies to filter \(high, low\) | | `serviceIds` | string | No | Comma-separated service IDs to filter | @@ -73,7 +72,6 @@ Get a single incident from PagerDuty by ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `incidentId` | string | Yes | ID of the incident to fetch | #### Output @@ -109,7 +107,6 @@ Create a new incident in PagerDuty. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `fromEmail` | string | Yes | Email address of a valid PagerDuty user | | `title` | string | Yes | Incident title/summary | | `serviceId` | string | Yes | ID of the PagerDuty service | @@ -145,7 +142,6 @@ Update an incident in PagerDuty (acknowledge, resolve, change urgency, etc.). | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `fromEmail` | string | Yes | Email address of a valid PagerDuty user | | `incidentId` | string | Yes | ID of the incident to update | | `status` | string | No | New status \(triggered, acknowledged, or resolved\) | @@ -178,7 +174,6 @@ Snooze a triggered PagerDuty incident for a number of seconds, after which it re | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `fromEmail` | string | Yes | Email address of a valid PagerDuty user | | `incidentId` | string | Yes | ID of the incident to snooze | | `duration` | string | Yes | Number of seconds to snooze the incident for \(1 to 604800\) | @@ -204,7 +199,6 @@ Merge one or more source incidents into a target incident. Source incidents are | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `fromEmail` | string | Yes | Email address of a valid PagerDuty user | | `targetIncidentId` | string | Yes | ID of the incident that will absorb the source incidents | | `sourceIncidentIds` | string | Yes | Comma-separated IDs of the incidents to merge into the target incident | @@ -231,7 +225,6 @@ Add a note to an existing PagerDuty incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `fromEmail` | string | Yes | Email address of a valid PagerDuty user | | `incidentId` | string | Yes | ID of the incident to add the note to | | `content` | string | Yes | Note content text | @@ -257,7 +250,6 @@ List the individual alerts attached to a PagerDuty incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `incidentId` | string | Yes | ID of the incident whose alerts to list | | `statuses` | string | No | Comma-separated statuses to filter \(triggered, resolved\) | | `limit` | string | No | Maximum number of results \(max 100\) | @@ -293,7 +285,6 @@ List services from PagerDuty with optional name filter. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `query` | string | No | Filter services by name | | `limit` | string | No | Maximum number of results \(max 100\) | | `offset` | string | No | Offset to start pagination search results | @@ -327,7 +318,6 @@ Get a single service from PagerDuty by ID. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `serviceId` | string | Yes | ID of the service to fetch | #### Output @@ -358,7 +348,6 @@ List current on-call entries from PagerDuty. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `escalationPolicyIds` | string | No | Comma-separated escalation policy IDs to filter | | `scheduleIds` | string | No | Comma-separated schedule IDs to filter | | `since` | string | No | Start time filter \(ISO 8601 format\) | @@ -396,7 +385,6 @@ List escalation policies from PagerDuty with an optional name filter. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `query` | string | No | Filter escalation policies by name | | `limit` | string | No | Maximum number of results \(max 100\) | | `offset` | string | No | Offset to start pagination search results | @@ -428,7 +416,6 @@ List on-call schedules from PagerDuty with an optional name filter. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `query` | string | No | Filter schedules by name | | `limit` | string | No | Maximum number of results \(max 100\) | | `offset` | string | No | Offset to start pagination search results | @@ -459,7 +446,6 @@ List users from PagerDuty with an optional name/email filter. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | PagerDuty REST API Key | | `query` | string | No | Filter users by name or email | | `limit` | string | No | Maximum number of results \(max 100\) | | `offset` | string | No | Offset to start pagination search results | diff --git a/apps/docs/content/docs/integrations/resend.mdx b/apps/docs/content/docs/integrations/resend.mdx index b3c1d2aad9d..cefddb4d4b3 100644 --- a/apps/docs/content/docs/integrations/resend.mdx +++ b/apps/docs/content/docs/integrations/resend.mdx @@ -2,7 +2,7 @@ title: Resend description: Send emails and manage contacts with Resend. integration: - type: "resend" + type: "resend_v2" color: "#181C1E" --- @@ -17,7 +17,7 @@ Use returned email IDs to check delivery status. The block's actions define the ## Use the block [#usage-instructions] -Send emails, retrieve email status, manage contacts, and view domains. Requires API Key. +Send emails, retrieve email status, manage contacts, and view domains. ## Actions @@ -41,7 +41,6 @@ Send an email using your own Resend API key and from address | `replyTo` | string | No | Reply-to email address | | `scheduledAt` | string | No | Schedule email to be sent later in ISO 8601 format | | `tags` | string | No | Comma-separated key:value pairs for email tags \(e.g., "category:welcome,type:onboarding"\) | -| `resendApiKey` | string | Yes | Resend API key for sending emails | #### Output @@ -66,7 +65,6 @@ Retrieve details of a previously sent email by its ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `emailId` | string | Yes | The ID of the email to retrieve | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -101,7 +99,6 @@ Cancel a scheduled email before it is sent | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `cancelEmailId` | string | Yes | The ID of the scheduled email to cancel | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -125,7 +122,6 @@ Create a new contact in Resend | `firstName` | string | No | First name of the contact | | `lastName` | string | No | Last name of the contact | | `unsubscribed` | boolean | No | Whether the contact is unsubscribed from all broadcasts | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -145,7 +141,6 @@ List all contacts in Resend | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -173,7 +168,6 @@ Retrieve details of a contact by ID or email | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `contactId` | string | Yes | The contact ID or email address to retrieve | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -202,7 +196,6 @@ Update an existing contact in Resend | `firstName` | string | No | Updated first name | | `lastName` | string | No | Updated last name | | `unsubscribed` | boolean | No | Whether the contact should be unsubscribed from all broadcasts | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -223,7 +216,6 @@ Delete a contact from Resend by ID or email | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `contactId` | string | Yes | The contact ID or email address to delete | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -245,7 +237,6 @@ Create a new audience in Resend | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `audienceName` | string | Yes | The name of the audience to create | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -267,7 +258,6 @@ Retrieve details of an audience by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `audienceId` | string | Yes | The ID of the audience to retrieve | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -289,7 +279,6 @@ List all audiences in Resend | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -314,7 +303,6 @@ Delete an audience from Resend by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `audienceId` | string | Yes | The ID of the audience to delete | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -343,7 +331,6 @@ Create a broadcast email for an audience in Resend | `broadcastReplyTo` | string | No | Reply-to email address | | `broadcastName` | string | No | Friendly internal name for the broadcast | | `broadcastPreviewText` | string | No | Preview text shown in the inbox before the email is opened | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -365,7 +352,6 @@ Send a broadcast immediately or schedule it for later | --------- | ---- | -------- | ----------- | | `broadcastId` | string | Yes | The ID of the broadcast to send | | `broadcastScheduledAt` | string | No | Schedule delivery in natural language \(e.g., "in 1 min"\) or ISO 8601 format. Sends immediately if omitted | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -386,7 +372,6 @@ Retrieve details of a broadcast by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `broadcastId` | string | Yes | The ID of the broadcast to retrieve | -| `resendApiKey` | string | Yes | Resend API key | #### Output @@ -417,7 +402,6 @@ List all verified domains in your Resend account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `resendApiKey` | string | Yes | Resend API key | #### Output diff --git a/apps/docs/content/docs/integrations/rootly.mdx b/apps/docs/content/docs/integrations/rootly.mdx index 1771c2d2c90..90796f70d1f 100644 --- a/apps/docs/content/docs/integrations/rootly.mdx +++ b/apps/docs/content/docs/integrations/rootly.mdx @@ -2,7 +2,7 @@ title: Rootly description: Manage incidents, alerts, and on-call with Rootly integration: - type: "rootly" + type: "rootly_v2" color: "#6C72C8" --- @@ -29,7 +29,6 @@ Create a new incident in Rootly with optional severity, services, and teams. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `title` | string | No | The title of the incident \(auto-generated if not provided\) | | `summary` | string | No | A summary of the incident | | `severityId` | string | No | Severity ID to attach to the incident | @@ -79,7 +78,6 @@ Retrieve a single incident by ID from Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to retrieve | #### Output @@ -118,7 +116,6 @@ Update an existing incident in Rootly (status, severity, summary, etc.). | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to update | | `title` | string | No | Updated incident title | | `summary` | string | No | Updated incident summary | @@ -172,7 +169,6 @@ List incidents from Rootly with optional filtering by status, severity, and more | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `status` | string | No | Filter by status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed\) | | `severity` | string | No | Filter by severity slug | | `search` | string | No | Search term to filter incidents | @@ -220,7 +216,6 @@ Create a new alert in Rootly for on-call notification and routing. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `summary` | string | Yes | The summary of the alert | | `description` | string | No | A detailed description of the alert | | `source` | string | No | The source of the alert \(e.g., api, manual, datadog, pagerduty\) | @@ -263,7 +258,6 @@ List alerts from Rootly with optional filtering by status, source, and services. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `status` | string | No | Filter by status \(open, triggered, acknowledged, resolved\) | | `source` | string | No | Filter by source \(e.g., api, datadog, pagerduty\) | | `services` | string | No | Filter by service slugs \(comma-separated\) | @@ -304,7 +298,6 @@ Add a timeline event to an existing incident in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to add the event to | | `event` | string | Yes | The summary/description of the event | | `visibility` | string | No | Event visibility \(internal or external\) | @@ -332,7 +325,6 @@ List services from Rootly with optional search filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter services | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -363,7 +355,6 @@ List severity levels configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter severities | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -396,7 +387,6 @@ List teams (groups) configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter teams | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -427,7 +417,6 @@ List environments configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter environments | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -458,7 +447,6 @@ List incident types configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Filter incident types by name | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -489,7 +477,6 @@ List functionalities configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter functionalities | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -520,7 +507,6 @@ List incident retrospectives (post-mortems) from Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `status` | string | No | Filter by status \(draft, published\) | | `search` | string | No | Search term to filter retrospectives | | `pageSize` | number | No | Number of items per page \(default: 20\) | @@ -554,7 +540,6 @@ Delete an incident by ID from Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to delete | #### Output @@ -576,7 +561,6 @@ Retrieve a single alert by ID from Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to retrieve | #### Output @@ -610,7 +594,6 @@ Update an existing alert in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to update | | `summary` | string | No | Updated alert summary | | `description` | string | No | Updated alert description | @@ -653,7 +636,6 @@ Acknowledge an alert in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to acknowledge | #### Output @@ -687,7 +669,6 @@ Resolve an alert in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to resolve | | `resolutionMessage` | string | No | Message describing how the alert was resolved | | `resolveRelatedIncidents` | boolean | No | Whether to also resolve related incidents | @@ -723,7 +704,6 @@ Create a new action item for an incident in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to add the action item to | | `summary` | string | Yes | The title of the action item | | `description` | string | No | A detailed description of the action item | @@ -760,7 +740,6 @@ List action items for an incident in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to list action items for | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -793,7 +772,6 @@ List users from Rootly with optional search and email filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter users | | `email` | string | No | Filter users by email address | | `pageSize` | number | No | Number of items per page \(default: 20\) | @@ -826,7 +804,6 @@ List current on-call entries from Rootly with optional filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `scheduleIds` | string | No | Comma-separated schedule IDs to filter by | | `escalationPolicyIds` | string | No | Comma-separated escalation policy IDs to filter by | | `userIds` | string | No | Comma-separated user IDs to filter by | @@ -859,7 +836,6 @@ List on-call schedules from Rootly with optional search filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter schedules | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -889,7 +865,6 @@ List escalation policies from Rootly with optional search filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter escalation policies | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -921,7 +896,6 @@ List causes from Rootly with optional search filtering. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter causes | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -952,7 +926,6 @@ List playbooks from Rootly with pagination support. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -981,7 +954,6 @@ Transition a Rootly incident to the mitigated state. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to mitigate | | `mitigationMessage` | string | No | How was the incident mitigated? | @@ -1021,7 +993,6 @@ Transition a Rootly incident to the resolved state. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident to resolve | | `resolutionMessage` | string | No | How was the incident resolved? | @@ -1061,7 +1032,6 @@ Assign an incident role (e.g. commander) to a user on a Rootly incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `userId` | string | Yes | The ID of the user to assign \(use List Users to find IDs\) | | `incidentRoleId` | string | Yes | The ID of the incident role \(use List Incident Roles to find IDs\) | @@ -1102,7 +1072,6 @@ Remove an incident role assignment from a user on a Rootly incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `userId` | string | Yes | The ID of the user to unassign \(use List Users to find IDs\) | | `incidentRoleId` | string | Yes | The ID of the incident role to remove \(use List Incident Roles to find IDs\) | @@ -1143,7 +1112,6 @@ Subscribe users to a Rootly incident so they receive updates. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `userIds` | string | Yes | Comma-separated user IDs to subscribe \(use List Users to find IDs\) | @@ -1183,7 +1151,6 @@ Unsubscribe users from a Rootly incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `userIds` | string | Yes | Comma-separated user IDs to unsubscribe \(use List Users to find IDs\) | @@ -1223,7 +1190,6 @@ Post a public status page update for a Rootly incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `event` | string | Yes | The status page update message to publish | | `statusPageId` | string | No | The ID of the status page to post to | @@ -1258,7 +1224,6 @@ Update a Rootly incident action item (status, priority, assignee, etc.). | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `actionItemId` | string | Yes | The ID of the action item to update | | `summary` | string | No | Updated action item title | | `description` | string | No | Updated description | @@ -1295,7 +1260,6 @@ Delete a Rootly incident action item. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `actionItemId` | string | Yes | The ID of the action item to delete | #### Output @@ -1317,7 +1281,6 @@ Snooze a Rootly alert for a set number of minutes. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to snooze | | `delayMinutes` | number | Yes | Number of minutes to snooze the alert | @@ -1352,7 +1315,6 @@ Escalate a Rootly alert, optionally to a specific escalation policy or level. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `alertId` | string | Yes | The ID of the alert to escalate | | `escalationPolicyId` | string | No | Escalation policy ID to escalate to \(use List Escalation Policies to find IDs\) | | `escalationPolicyLevel` | number | No | Escalation policy level to escalate to | @@ -1388,7 +1350,6 @@ List the timeline events for a Rootly incident. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `incidentId` | string | Yes | The ID of the incident | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -1418,7 +1379,6 @@ Trigger a Rootly automation workflow, optionally scoped to an incident or alert. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `workflowId` | string | Yes | The ID of the workflow to run | | `incidentId` | string | No | Incident ID to run the workflow against | | `alertId` | string | No | Alert ID to run the workflow against | @@ -1454,7 +1414,6 @@ List incident roles configured in Rootly (e.g. commander, scribe). | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Rootly API key | | `search` | string | No | Search term to filter incident roles | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | diff --git a/apps/docs/content/docs/integrations/sentry.mdx b/apps/docs/content/docs/integrations/sentry.mdx index 4c7120ad937..eb30a0e5a9a 100644 --- a/apps/docs/content/docs/integrations/sentry.mdx +++ b/apps/docs/content/docs/integrations/sentry.mdx @@ -2,7 +2,7 @@ title: Sentry description: Manage Sentry issues, projects, events, and releases integration: - type: "sentry" + type: "sentry_v2" color: "#362D59" --- @@ -29,7 +29,6 @@ List issues from Sentry for a specific organization and optionally a specific pr | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | No | Filter issues by numeric project ID \(e.g., "4501234"\). This organization-scoped endpoint requires the numeric project ID, not the project slug. | | `query` | string | No | Search query to filter issues. Supports Sentry search syntax \(e.g., "is:unresolved", "level:error"\) | @@ -98,7 +97,6 @@ Retrieve detailed information about a specific Sentry issue by its ID. Returns c | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `issueId` | string | Yes | The unique ID of the issue to retrieve \(e.g., "12345"\) | @@ -158,7 +156,6 @@ Update a Sentry issue by changing its status, assignment, bookmark state, or oth | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `issueId` | string | Yes | The unique ID of the issue to update \(e.g., "12345"\) | | `status` | string | No | New status for the issue: resolved, unresolved, ignored, or resolvedInNextRelease | @@ -199,7 +196,6 @@ List all projects in a Sentry organization. Returns project details including na | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `cursor` | string | No | Pagination cursor for retrieving next page of results | | `limit` | number | No | Number of projects to return per page \(default: 25, max: 100\) | @@ -243,7 +239,6 @@ Retrieve detailed information about a specific Sentry project by its slug. Retur | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | Yes | The slug of the project to retrieve \(e.g., "my-project"\) | @@ -298,7 +293,6 @@ Create a new Sentry project in an organization. Requires a team to associate the | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `name` | string | Yes | The name of the project | | `teamSlug` | string | Yes | The slug of the team that will own this project | @@ -349,7 +343,6 @@ Update a Sentry project by changing its name, slug, platform, or other settings. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | Yes | The slug of the project to update \(e.g., "my-project"\) | | `name` | string | No | New name for the project | @@ -390,7 +383,6 @@ List events from a Sentry project. Can be filtered by issue ID, query, or time p | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | Yes | The slug of the project to list events from \(e.g., "my-project"\) | | `issueId` | string | No | Filter events by a specific issue ID \(e.g., "12345"\) | @@ -455,7 +447,6 @@ Retrieve detailed information about a specific Sentry event by its ID. Returns c | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | Yes | The slug of the project \(e.g., "my-project"\) | | `eventId` | string | Yes | The unique ID of the event to retrieve \(e.g., "abc123def456"\) | @@ -513,7 +504,6 @@ List releases for a Sentry organization or project. Returns release details incl | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `projectSlug` | string | No | Filter releases by numeric project ID \(e.g., "4501234"\). This organization-scoped endpoint requires the numeric project ID, not the project slug. | | `query` | string | No | Search query to filter releases \(e.g., "1.0" to match version patterns\) | @@ -581,7 +571,6 @@ Create a new release in Sentry. A release is a version of your code deployed to | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `version` | string | Yes | Version identifier for the release \(e.g., "2.0.0", "my-app@1.0.0", or a git commit SHA\) | | `projects` | string | Yes | Comma-separated list of project slugs to associate with this release | @@ -648,7 +637,6 @@ Create a deploy record for a Sentry release in a specific environment. Deploys t | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `version` | string | Yes | Version identifier of the release being deployed \(e.g., "1.0.0" or "abc123"\) | | `environment` | string | Yes | Environment name where the release is being deployed \(e.g., "production", "staging"\) | @@ -681,7 +669,6 @@ List all teams in a Sentry organization. Useful for discovering the team slug re | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Sentry API authentication token | | `organizationSlug` | string | Yes | The slug of the organization \(e.g., "my-org"\) | | `query` | string | No | Filter teams by name or slug | | `cursor` | string | No | Pagination cursor for retrieving next page of results | diff --git a/apps/docs/content/docs/integrations/stripe.mdx b/apps/docs/content/docs/integrations/stripe.mdx index df414b2abc2..7b996eee532 100644 --- a/apps/docs/content/docs/integrations/stripe.mdx +++ b/apps/docs/content/docs/integrations/stripe.mdx @@ -2,7 +2,7 @@ title: Stripe description: Process payments and manage Stripe data integration: - type: "stripe" + type: "stripe_v2" color: "#635BFF" --- @@ -29,7 +29,6 @@ Create a new Payment Intent to process a payment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `amount` | number | Yes | Amount in the currency’s smallest unit \(e.g., 2000 for 20.00 USD\) | | `currency` | string | Yes | Three-letter ISO currency code \(e.g., usd, eur\) | | `customer` | string | No | Customer ID to associate with this payment | @@ -108,7 +107,6 @@ Retrieve an existing Payment Intent by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Payment Intent ID \(e.g., pi_1234567890\) | #### Output @@ -180,7 +178,6 @@ Update an existing Payment Intent | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Payment Intent ID \(e.g., pi_1234567890\) | | `amount` | number | No | Updated amount in the currency’s smallest unit | | `currency` | string | No | Three-letter ISO currency code | @@ -257,7 +254,6 @@ Confirm a Payment Intent and inspect its resulting status. Confirmation can requ | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Payment Intent ID \(e.g., pi_1234567890\) | | `payment_method` | string | No | Payment method ID to confirm with | @@ -330,7 +326,6 @@ Capture an authorized Payment Intent | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Payment Intent ID \(e.g., pi_1234567890\) | | `amount_to_capture` | number | No | Amount to capture in the currency’s smallest unit \(defaults to the full amount\) | @@ -403,7 +398,6 @@ Cancel a Payment Intent | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Payment Intent ID \(e.g., pi_1234567890\) | | `cancellation_reason` | string | No | Reason for cancellation \(duplicate, fraudulent, requested_by_customer, abandoned\) | @@ -476,7 +470,6 @@ List all Payment Intents | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `customer` | string | No | Filter by customer ID | | `created` | json | No | Filter by creation date \(e.g., \{"gt": 1633024800\}\) | @@ -548,7 +541,6 @@ Search for Payment Intents using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "status:'succeeded' AND currency:'usd'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -619,7 +611,6 @@ Create a new customer object | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `email` | string | No | Customer email address | | `name` | string | No | Customer full name | | `phone` | string | No | Customer phone number | @@ -687,7 +678,6 @@ Retrieve an existing customer by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Customer ID \(e.g., cus_1234567890\) | #### Output @@ -749,7 +739,6 @@ Update an existing customer | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Customer ID \(e.g., cus_1234567890\) | | `email` | string | No | Updated email address | | `name` | string | No | Updated name | @@ -817,7 +806,6 @@ Permanently delete a customer | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Customer ID \(e.g., cus_1234567890\) | #### Output @@ -839,7 +827,6 @@ List all customers | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `email` | string | No | Filter by email address | | `created` | json | No | Filter by creation date | @@ -902,7 +889,6 @@ Search for customers using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "email:'customer@example.com'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -964,7 +950,6 @@ Create a new subscription for a customer | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `customer` | string | Yes | Customer ID to subscribe | | `items` | json | Yes | Array of items with price IDs \(e.g., \[\{"price": "price_xxx", "quantity": 1\}\]\) | | `trial_period_days` | number | No | Number of trial days | @@ -1050,7 +1035,6 @@ Retrieve an existing subscription by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Subscription ID \(e.g., sub_1234567890\) | #### Output @@ -1131,7 +1115,6 @@ Update an existing subscription | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Subscription ID \(e.g., sub_1234567890\) | | `items` | json | No | Updated array of items with price IDs | | `cancel_at_period_end` | boolean | No | Cancel subscription at period end | @@ -1215,7 +1198,6 @@ Cancel a subscription | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Subscription ID \(e.g., sub_1234567890\) | | `prorate` | boolean | No | Whether to prorate the cancellation | | `invoice_now` | boolean | No | Whether to invoice immediately | @@ -1298,7 +1280,6 @@ Resume a paused subscription | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Subscription ID \(e.g., sub_1234567890\) | #### Output @@ -1379,7 +1360,6 @@ List one page of subscriptions. Follow starting_after while metadata.has_more is | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `customer` | string | No | Filter by customer ID | | `status` | string | No | Filter by status \(active, past_due, unpaid, canceled, incomplete, incomplete_expired, trialing, all\) | @@ -1464,7 +1444,6 @@ Search one page of subscriptions using query syntax. Pass metadata.next_page as | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "status:'active' AND metadata\['order_id'\]:'6735'"\) | | `page` | string | No | Pagination token from metadata.next_page. Omit for the first page and keep the same query. | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -1547,7 +1526,6 @@ Create a new invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `customer` | string | Yes | Customer ID \(e.g., cus_1234567890\) | | `description` | string | No | Description of the invoice | | `metadata` | json | No | Set of key-value pairs | @@ -1711,7 +1689,6 @@ Retrieve an existing invoice by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | #### Output @@ -1871,7 +1848,6 @@ Update an existing invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | | `description` | string | No | Description of the invoice | | `metadata` | json | No | Set of key-value pairs | @@ -2034,7 +2010,6 @@ Permanently delete a draft invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | #### Output @@ -2056,7 +2031,6 @@ Finalize a draft invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | | `auto_advance` | boolean | No | Auto-advance the invoice | @@ -2217,7 +2191,6 @@ Pay an invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | | `paid_out_of_band` | boolean | No | Mark invoice as paid out of band | @@ -2378,7 +2351,6 @@ Void an invoice | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | #### Output @@ -2538,7 +2510,6 @@ Send an invoice to the customer | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Invoice ID \(e.g., in_1234567890\) | #### Output @@ -2564,7 +2535,6 @@ List all invoices | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `customer` | string | No | Filter by customer ID | | `status` | string | No | Filter by invoice status | @@ -2590,7 +2560,6 @@ Search for invoices using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "customer:'cus_1234567890'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -2615,7 +2584,6 @@ Create a new charge to process a payment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `amount` | number | Yes | Amount in the currency’s smallest unit \(e.g., 2000 for 20.00 USD\) | | `currency` | string | Yes | Three-letter ISO currency code \(e.g., usd, eur\) | | `customer` | string | No | Customer ID to associate with this charge | @@ -2648,7 +2616,6 @@ Retrieve an existing charge by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Charge ID \(e.g., ch_1234567890\) | #### Output @@ -2675,7 +2642,6 @@ Update an existing charge | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Charge ID \(e.g., ch_1234567890\) | | `description` | string | No | Updated description | | `metadata` | json | No | Updated metadata | @@ -2704,7 +2670,6 @@ Capture an uncaptured charge | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Charge ID \(e.g., ch_1234567890\) | | `amount` | number | No | Amount to capture in the currency’s smallest unit \(defaults to the full amount\) | @@ -2732,7 +2697,6 @@ List all charges | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `customer` | string | No | Filter by customer ID | | `created` | json | No | Filter by creation date \(e.g., \{"gt": 1633024800\}\) | @@ -2758,7 +2722,6 @@ Search for charges using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "status:'succeeded' AND currency:'usd'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -2783,7 +2746,6 @@ Create a new product object | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `name` | string | Yes | Product name | | `description` | string | No | Product description | | `active` | boolean | No | Whether the product is active | @@ -2812,7 +2774,6 @@ Retrieve an existing product by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Product ID \(e.g., prod_1234567890\) | #### Output @@ -2837,7 +2798,6 @@ Update an existing product | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Product ID \(e.g., prod_1234567890\) | | `name` | string | No | Updated product name | | `description` | string | No | Updated product description | @@ -2867,7 +2827,6 @@ Permanently delete a product | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Product ID \(e.g., prod_1234567890\) | #### Output @@ -2889,7 +2848,6 @@ List all products | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `active` | boolean | No | Filter by active status | @@ -2914,7 +2872,6 @@ Search for products using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "name:'shirt'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -2939,7 +2896,6 @@ Create a new price for a product | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `product` | string | Yes | Product ID \(e.g., prod_1234567890\) | | `currency` | string | Yes | Three-letter ISO currency code \(e.g., usd, eur\) | | `unit_amount` | number | No | Amount in the currency’s smallest unit \(e.g., 1000 for 10.00 USD\) | @@ -2970,7 +2926,6 @@ Retrieve an existing price by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Price ID \(e.g., price_1234567890\) | #### Output @@ -2996,7 +2951,6 @@ Update an existing price | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Price ID \(e.g., price_1234567890\) | | `active` | boolean | No | Whether the price is active | | `metadata` | json | No | Updated metadata | @@ -3024,7 +2978,6 @@ List all prices | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `product` | string | No | Filter by product ID | | `active` | boolean | No | Filter by active status | @@ -3050,7 +3003,6 @@ Search for prices using query syntax | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `query` | string | Yes | Search query \(e.g., "active:'true' AND currency:'usd'"\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | @@ -3075,7 +3027,6 @@ Retrieve an existing Event by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `id` | string | Yes | Event ID \(e.g., evt_1234567890\) | #### Output @@ -3100,7 +3051,6 @@ List all Events | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Stripe API key \(secret key\) | | `limit` | number | No | Number of results to return \(default 10, max 100\) | | `type` | string | No | Filter by event type \(e.g., payment_intent.created\) | | `created` | json | No | Filter by creation date \(e.g., \{"gt": 1633024800\}\) | diff --git a/apps/docs/content/docs/integrations/telegram.mdx b/apps/docs/content/docs/integrations/telegram.mdx index 33480512415..0701da3bf4f 100644 --- a/apps/docs/content/docs/integrations/telegram.mdx +++ b/apps/docs/content/docs/integrations/telegram.mdx @@ -2,7 +2,7 @@ title: Telegram description: Interact with Telegram integration: - type: "telegram" + type: "telegram_v2" color: "#FFFFFF" --- @@ -27,7 +27,7 @@ The [webhook setup video](https://www.youtube.com/embed/9oKcJtQ0_IM) also demons 1. Add the bot to the group and grant the permissions needed by the action. 2. Obtain the group's numeric chat ID from a bot message update, such as the trigger's `message.raw.chat.id`. Group IDs can be negative. -3. Add a Telegram action block, select **Send Message**, and set **Bot Token**, **Chat ID**, and **Message**. +3. Add a Telegram action block, select **Send Message**, choose a **Telegram Credential** (a saved bot token), and set **Chat ID** and **Message**. 4. Run the workflow and verify the message in the group. The [group messaging video](https://www.youtube.com/embed/AG55LpUreGI) demonstrates the block configuration. Bot privacy settings affect which group messages Telegram delivers; receiving a command addressed to the bot can help verify the trigger before changing those settings. @@ -49,7 +49,6 @@ Send a text message through the Telegram Bot API. Markdown is converted to Teleg | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `text` | string | Yes | Message text to send | @@ -85,7 +84,6 @@ Delete messages in Telegram channels or chats through the Telegram Bot API. Requ | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `messageId` | string | Yes | Telegram message ID \(numeric identifier of the message to delete\) | @@ -110,7 +108,6 @@ Send photos to Telegram channels or users through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `photo` | string | Yes | Photo to send. Pass a file_id or HTTP URL | | `caption` | string | No | Photo caption \(optional\) | @@ -153,7 +150,6 @@ Send videos to Telegram channels or users through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `video` | string | Yes | Video to send. Pass a file_id or HTTP URL | | `caption` | string | No | Video caption \(optional\) | @@ -229,7 +225,6 @@ Send audio files to Telegram channels or users through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `audio` | string | Yes | Audio file to send. Pass a file_id or HTTP URL | | `caption` | string | No | Audio caption \(optional\) | @@ -275,7 +270,6 @@ Send animations (GIFs) to Telegram channels or users through the Telegram Bot AP | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `animation` | string | Yes | Animation to send. Pass a file_id or HTTP URL | | `caption` | string | No | Animation caption \(optional\) | @@ -351,7 +345,6 @@ Send documents (PDF, ZIP, DOC, etc.) to Telegram channels or users through the T | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `files` | file[] | No | Document file to send \(PDF, ZIP, DOC, etc.\). Max size: 50MB | | `caption` | string | No | Document caption \(optional\) | @@ -394,7 +387,6 @@ Edit the text of an existing message in a Telegram chat or channel through the T | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `messageId` | number | Yes | Identifier of the message to edit | | `text` | string | Yes | New text of the message | @@ -421,7 +413,6 @@ Forward a message from one Telegram chat to another through the Telegram Bot API | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Destination chat ID \(numeric, can be negative for groups\) | | `fromChatId` | string | Yes | Source chat ID the original message belongs to | | `messageId` | number | Yes | Identifier of the message to forward in the source chat | @@ -448,7 +439,6 @@ Copy a message to another Telegram chat without a forward header through the Tel | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Destination chat ID \(numeric, can be negative for groups\) | | `fromChatId` | string | Yes | Source chat ID the original message belongs to | | `messageId` | number | Yes | Identifier of the message to copy in the source chat | @@ -474,7 +464,6 @@ Send a point on the map to a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `latitude` | number | Yes | Latitude of the location | | `longitude` | number | Yes | Longitude of the location | @@ -500,7 +489,6 @@ Send a phone contact to a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `phoneNumber` | string | Yes | Contact's phone number | | `firstName` | string | Yes | Contact's first name | @@ -528,7 +516,6 @@ Send a native poll to a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `question` | string | Yes | Poll question \(1-300 characters\) | | `options` | json | Yes | List of 2-10 answer options as text strings | @@ -556,7 +543,6 @@ Pin a message in a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `messageId` | number | Yes | Identifier of the message to pin | | `disableNotification` | boolean | No | Pass true to pin silently without notifying chat members | @@ -582,7 +568,6 @@ Unpin a pinned message in a Telegram chat through the Telegram Bot API. Unpins t | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `messageId` | number | No | Identifier of the message to unpin \(omit to unpin the most recent one\) | @@ -607,7 +592,6 @@ Set or remove an emoji reaction on a message in a Telegram chat through the Tele | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `messageId` | number | Yes | Identifier of the target message | | `reaction` | string | No | Emoji to react with \(leave empty to remove the reaction\) | @@ -634,7 +618,6 @@ Show a status action such as a typing indicator in a Telegram chat through the T | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID \(numeric, can be negative for groups\) | | `action` | string | Yes | Type of action to broadcast \(e.g. typing, upload_photo, record_video, upload_document, find_location\) | @@ -659,7 +642,6 @@ Get up-to-date information about a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID or @username \(numeric, can be negative for groups\) | #### Output @@ -691,7 +673,6 @@ Get information about a member of a Telegram chat through the Telegram Bot API. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Your Telegram Bot API Token | | `chatId` | string | Yes | Telegram chat ID or @username \(numeric, can be negative for groups\) | | `userId` | number | Yes | Unique identifier of the target user | diff --git a/apps/docs/content/docs/integrations/typeform.mdx b/apps/docs/content/docs/integrations/typeform.mdx index 5748dc50521..c7840979332 100644 --- a/apps/docs/content/docs/integrations/typeform.mdx +++ b/apps/docs/content/docs/integrations/typeform.mdx @@ -2,7 +2,7 @@ title: Typeform description: Interact with Typeform integration: - type: "typeform" + type: "typeform_v2" color: "#262627" --- @@ -10,12 +10,12 @@ import { ReferenceDetails } from "@/components/ui/reference-details" {/* MANUAL-CONTENT-START:intro */} -Use [Typeform](https://www.typeform.com/) in Sim to retrieve form responses, download submitted files, and get form insights. Switch to trigger mode to start a workflow when a form is submitted. The integration requires an API key. +Use [Typeform](https://www.typeform.com/) in Sim to retrieve form responses, download submitted files, and get form insights. Switch to trigger mode to start a workflow when a form is submitted. Actions sign in with a saved Typeform credential (a personal access token with the `forms:read` scope); the trigger takes its own token. {/* MANUAL-CONTENT-END */} ## Use the block [#usage-instructions] -Retrieve responses, download files, and get form insights. Use trigger mode to start a workflow when a form is submitted. Requires API Key. +Retrieve responses, download files, and get form insights. Use trigger mode to start a workflow when a form is submitted. ## Actions @@ -30,7 +30,6 @@ Retrieve form responses from Typeform | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `formId` | string | Yes | Typeform form ID \(e.g., "abc123XYZ"\) | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `pageSize` | number | No | Number of responses to retrieve \(e.g., 10, 25, 50\) | | `before` | string | No | Cursor token for fetching the next page of older responses | | `after` | string | No | Cursor token for fetching the next page of newer responses | @@ -63,7 +62,6 @@ Download files uploaded in Typeform responses | `fieldId` | string | Yes | Unique ID of the file upload field | | `filename` | string | Yes | Filename of the uploaded file | | `inline` | boolean | No | Whether to request the file with inline Content-Disposition | -| `apiKey` | string | Yes | Typeform Personal Access Token | #### Output @@ -87,7 +85,6 @@ Retrieve insights and analytics for Typeform forms | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `formId` | string | Yes | Typeform form ID \(e.g., "abc123XYZ"\) | -| `apiKey` | string | Yes | Typeform Personal Access Token | #### Output @@ -128,7 +125,6 @@ Retrieve a list of all forms in your Typeform account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `search` | string | No | Search query to filter forms by title \(e.g., "Customer Feedback"\) | | `page` | number | No | Page number for pagination \(e.g., 1, 2, 3\) | | `pageSize` | number | No | Number of forms per page \(e.g., 10, 25, 50, max: 200\) | @@ -154,7 +150,6 @@ Retrieve complete details and structure of a specific form | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `formId` | string | Yes | Form unique identifier \(e.g., "abc123XYZ"\) | #### Output @@ -187,7 +182,6 @@ Create a new form with fields and settings | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `title` | string | Yes | Form title | | `type` | string | No | Form type \(default: "form"\). Options: "form", "quiz" | | `workspaceId` | string | No | Workspace ID to create the form in \(e.g., "ws_abc123"\) | @@ -222,7 +216,6 @@ Update an existing form using JSON Patch operations | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `formId` | string | Yes | Form unique identifier to update \(e.g., "abc123XYZ"\) | | `operations` | json | Yes | Array of JSON Patch operations \(RFC 6902\). Each operation needs: op \(add/remove/replace\), path, and value \(for add/replace\) | @@ -244,7 +237,6 @@ Permanently delete a form and all its responses | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Typeform Personal Access Token | | `formId` | string | Yes | Form unique identifier to delete \(e.g., "abc123XYZ"\) | #### Output diff --git a/apps/docs/content/docs/integrations/vercel.mdx b/apps/docs/content/docs/integrations/vercel.mdx index 86904e29cbc..97878eafb04 100644 --- a/apps/docs/content/docs/integrations/vercel.mdx +++ b/apps/docs/content/docs/integrations/vercel.mdx @@ -2,7 +2,7 @@ title: Vercel description: Manage Vercel deployments, projects, and infrastructure integration: - type: "vercel" + type: "vercel_v2" color: "#171717" --- @@ -29,7 +29,6 @@ List deployments for a Vercel project or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | No | Filter deployments by project ID or name | | `target` | string | No | Filter by environment: production or staging | | `state` | string | No | Filter by state: BUILDING, ERROR, INITIALIZING, QUEUED, READY, CANCELED, BLOCKED | @@ -77,7 +76,6 @@ Get details of a specific Vercel deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | The unique deployment identifier or hostname | | `withGitRepoInfo` | string | No | Whether to add in gitRepo information \(true/false\) | | `teamId` | string | No | Team ID to scope the request | @@ -136,7 +134,6 @@ Create a new deployment or redeploy an existing one | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `name` | string | Yes | Project name for the deployment | | `project` | string | No | Project ID \(overrides name for project lookup\) | | `deploymentId` | string | No | Existing deployment ID to redeploy | @@ -175,7 +172,6 @@ Cancel a running Vercel deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | The deployment ID to cancel | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -204,7 +200,6 @@ Delete a Vercel deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | The deployment ID or URL to delete | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -228,7 +223,6 @@ Get build and runtime events for a Vercel deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | The unique deployment identifier or hostname | | `direction` | string | No | Order of events by timestamp: backward or forward \(default: forward\) | | `follow` | number | No | When set to 1, returns live events as they happen | @@ -266,7 +260,6 @@ List files in a Vercel deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | The deployment ID to list files for | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -299,7 +292,6 @@ Promote a deployment by pointing the production deployment to the given deployme | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `deploymentId` | string | Yes | The ID of the deployment to promote to production | | `teamId` | string | No | Team ID to scope the request | @@ -323,7 +315,6 @@ List all projects in a Vercel team or account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `search` | string | No | Search projects by name | | `limit` | number | No | Maximum number of projects to return | | `from` | string | No | Continuation token for pagination, taken from the previous response's pagination.next value. Query only projects updated after this timestamp or continuation token. | @@ -358,7 +349,6 @@ Get details of a specific Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -391,7 +381,6 @@ Create a new Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `name` | string | Yes | Project name | | `framework` | string | No | Project framework \(e.g. nextjs, remix, vite\) | | `gitRepository` | json | No | Git repository connection object with type and repo | @@ -426,7 +415,6 @@ Update an existing Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `name` | string | No | New project name | | `framework` | string | No | Project framework \(e.g. nextjs, remix, vite\) | @@ -460,7 +448,6 @@ Delete a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -483,7 +470,6 @@ Pause a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -508,7 +494,6 @@ Unpause a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -533,7 +518,6 @@ List all domains for a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -573,7 +557,6 @@ Add a domain to a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `domain` | string | Yes | Domain name to add | | `redirect` | string | No | Target domain for redirect | @@ -613,7 +596,6 @@ Remove a domain from a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `domain` | string | Yes | Domain name to remove | | `teamId` | string | No | Team ID to scope the request | @@ -637,7 +619,6 @@ Update a project domain's configuration on Vercel | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `domain` | string | Yes | Domain name to update | | `redirect` | string | No | Target destination domain for redirect | @@ -677,7 +658,6 @@ Verify a Vercel project domain by checking its verification challenge | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `domain` | string | Yes | Domain name to verify | | `teamId` | string | No | Team ID to scope the request | @@ -709,7 +689,6 @@ Retrieve environment variables for a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `decrypt` | boolean | No | If true, decrypted variable values are returned instead of ciphertext | | `gitBranch` | string | No | Filter results to the environment variables for this git branch \(must have target=preview\) | @@ -744,7 +723,6 @@ Create an environment variable for a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `key` | string | Yes | Environment variable name | | `value` | string | Yes | Environment variable value | @@ -781,7 +759,6 @@ Update an environment variable for a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `envId` | string | Yes | Environment variable ID to update | | `key` | string | No | New variable name | @@ -819,7 +796,6 @@ Delete an environment variable from a Vercel project | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | Yes | Project ID or name | | `envId` | string | Yes | Environment variable ID to delete | | `teamId` | string | No | Team ID to scope the request | @@ -843,7 +819,6 @@ List all domains in a Vercel account or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `limit` | number | No | Maximum number of domains to return \(default 20\) | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -887,7 +862,6 @@ Get information about a specific domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name to retrieve | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -928,7 +902,6 @@ Add a new domain to a Vercel account or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `name` | string | Yes | The domain name to add | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -966,7 +939,6 @@ Delete a domain from a Vercel account or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name to delete | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -990,7 +962,6 @@ Get the configuration for a domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name to get configuration for | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -1021,7 +992,6 @@ List all DNS records for a domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name to list records for | | `limit` | number | No | Maximum number of records to return | | `teamId` | string | No | Team ID to scope the request | @@ -1059,7 +1029,6 @@ Create a DNS record for a domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name to create the record for | | `recordName` | string | Yes | The subdomain or record name | | `recordType` | string | Yes | DNS record type \(A, AAAA, ALIAS, CAA, CNAME, HTTPS, MX, SRV, TXT, NS\) | @@ -1096,7 +1065,6 @@ Update an existing DNS record for a domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `recordId` | string | Yes | The ID of the DNS record to update | | `name` | string | No | The name of the DNS record | | `value` | string | No | The value of the DNS record | @@ -1141,7 +1109,6 @@ Delete a DNS record for a domain in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `domain` | string | Yes | The domain name the record belongs to | | `recordId` | string | Yes | The ID of the DNS record to delete | | `teamId` | string | No | Team ID to scope the request | @@ -1165,7 +1132,6 @@ List aliases for a Vercel project or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | No | Filter aliases by project ID | | `domain` | string | No | Filter aliases by domain | | `limit` | number | No | Maximum number of aliases to return | @@ -1203,7 +1169,6 @@ Get details about a specific alias by ID or hostname | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `aliasId` | string | Yes | Alias ID or hostname to look up | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -1236,7 +1201,6 @@ Assign an alias (domain/subdomain) to a deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID to assign the alias to | | `alias` | string | Yes | The domain or subdomain to assign as an alias | | `redirect` | string | No | Hostname to 307-redirect the alias to instead of serving the deployment directly | @@ -1264,7 +1228,6 @@ Delete an alias by its ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `aliasId` | string | Yes | Alias ID to delete | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -1287,7 +1250,6 @@ List all Edge Config stores for a team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `teamId` | string | No | Team ID to scope the request | #### Output @@ -1317,7 +1279,6 @@ Get details about a specific Edge Config store | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `edgeConfigId` | string | Yes | Edge Config ID to look up | | `teamId` | string | No | Team ID to scope the request | @@ -1346,7 +1307,6 @@ Create a new Edge Config store | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `slug` | string | Yes | The name/slug for the new Edge Config | | `teamId` | string | No | Team ID to scope the request | @@ -1375,7 +1335,6 @@ Get all items in an Edge Config store | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `edgeConfigId` | string | Yes | Edge Config ID to get items from | | `teamId` | string | No | Team ID to scope the request | @@ -1404,7 +1363,6 @@ Create, update, upsert, or delete items in an Edge Config store | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `edgeConfigId` | string | Yes | Edge Config ID to update items in | | `items` | json | Yes | Array of operations: \[\{operation: "create"\|"update"\|"upsert"\|"delete", key: string, value?: any\}\] | | `teamId` | string | No | Team ID to scope the request | @@ -1427,7 +1385,6 @@ Delete an Edge Config store by ID | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `edgeConfigId` | string | Yes | Edge Config ID to delete | | `teamId` | string | No | Team ID to scope the request | @@ -1449,7 +1406,6 @@ List webhooks for a Vercel project or team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `projectId` | string | No | Filter webhooks by project ID | | `teamId` | string | No | Team ID to scope the request | @@ -1480,7 +1436,6 @@ Get details about a specific Vercel webhook | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `webhookId` | string | Yes | Webhook ID to look up | | `teamId` | string | No | Team ID to scope the request | @@ -1508,7 +1463,6 @@ Create a new webhook for a Vercel team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `url` | string | Yes | Webhook URL \(must be https\) | | `events` | string | Yes | Comma-separated event names to subscribe to | | `projectIds` | string | No | Comma-separated project IDs to scope the webhook to | @@ -1539,7 +1493,6 @@ Delete a webhook from a Vercel team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `webhookId` | string | Yes | The webhook ID to delete | | `teamId` | string | No | Team ID to scope the request | @@ -1561,7 +1514,6 @@ Create a new deployment check | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID to create the check for | | `name` | string | Yes | Name of the check \(max 100 characters\) | | `blocking` | boolean | Yes | Whether the check blocks the deployment | @@ -1605,7 +1557,6 @@ Get details of a specific deployment check | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID the check belongs to | | `checkId` | string | Yes | Check ID to retrieve | | `teamId` | string | No | Team ID to scope the request | @@ -1644,7 +1595,6 @@ List all checks for a deployment | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID to list checks for | | `teamId` | string | No | Team ID to scope the request | | `slug` | string | No | Team slug to scope the request \(alternative to teamId\) | @@ -1684,7 +1634,6 @@ Update an existing deployment check | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID the check belongs to | | `checkId` | string | Yes | Check ID to update | | `name` | string | No | Updated name of the check | @@ -1730,7 +1679,6 @@ Rerequest a deployment check | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `deploymentId` | string | Yes | Deployment ID the check belongs to | | `checkId` | string | Yes | Check ID to rerequest | | `teamId` | string | No | Team ID to scope the request | @@ -1755,7 +1703,6 @@ List all teams in a Vercel account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `limit` | number | No | Maximum number of teams to return | | `since` | number | No | Timestamp in milliseconds to only include teams created since then | | `until` | number | No | Timestamp in milliseconds to only include teams created until then | @@ -1798,7 +1745,6 @@ Get information about a specific Vercel team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `teamId` | string | Yes | The team ID to retrieve | #### Output @@ -1837,7 +1783,6 @@ List all members of a Vercel team | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | | `teamId` | string | Yes | The team ID to list members for | | `limit` | number | No | Maximum number of members to return | | `role` | string | No | Filter by role \(OWNER, MEMBER, DEVELOPER, SECURITY, BILLING, VIEWER, VIEWER_FOR_PLUS, CONTRIBUTOR\) | @@ -1881,7 +1826,6 @@ Get information about the authenticated Vercel user | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | Vercel Access Token | #### Output diff --git a/apps/docs/content/docs/integrations/whatsapp.mdx b/apps/docs/content/docs/integrations/whatsapp.mdx index 1376f6d7014..73c43736eb5 100644 --- a/apps/docs/content/docs/integrations/whatsapp.mdx +++ b/apps/docs/content/docs/integrations/whatsapp.mdx @@ -2,7 +2,7 @@ title: WhatsApp description: Send WhatsApp messages integration: - type: "whatsapp" + type: "whatsapp_v2" color: "#25D366" --- diff --git a/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx b/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx index 16728f04a4b..bef679c734a 100644 --- a/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx +++ b/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx @@ -167,10 +167,10 @@ Saved AWS and Databricks credentials can trust Sim through OpenID Connect instea | Variable | Description | | --- | --- | | `WORKLOAD_IDENTITY_SIGNING_KEY` | PKCS#8 PEM RSA private key that signs Sim's workload-identity tokens: `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048` | -| `WORKLOAD_IDENTITY_ISSUER` | Issuer URL clouds trust. Defaults to `/oidc/workload` and must be reachable from the public internet. Never change it once trust policies reference it. | -| `WORKLOAD_IDENTITY_PREVIOUS_KEY` | During rotation, the retiring key (private or public PEM), still published so tokens it signed keep verifying | +| `WORKLOAD_IDENTITY_ISSUER` | Issuer URL clouds trust. Defaults to `/oidc/workload`; must be `https` on the default port, with no query or fragment, and reachable from the public internet, or keyless stays off. Never change it once trust policies reference it. | +| `WORKLOAD_IDENTITY_PREVIOUS_KEY` | A second published key (private or public PEM) that never signs: the incoming key before a rotation, or the retiring key after it | -See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments). +See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments) for setup and the rotation steps. ## Access control diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx index 747d3b1fb60..13e3bd8a199 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx @@ -1,6 +1,6 @@ 'use client' -import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react' +import { type ReactNode, useEffect, useRef, useState } from 'react' import { Chip, ChipDropdown, ChipLink, cn } from '@sim/emcn' import { ArrowLeft, Plus } from '@sim/emcn/icons' import { useRouter } from 'next/navigation' @@ -174,9 +174,9 @@ function IntegrationBlockDetailContent({ integration, workspaceId }: Integration const oauthAvailable = Boolean(oauthService) && (availability?.oauthAvailable ?? true) const [oauthOpen, setOAuthOpen] = useState(false) const [personalTokenOpen, setPersonalTokenOpen] = useState(false) - const personalTokenAvailable = - integration.type === 'gitlab' && - (availability?.state === 'ready' || availability?.state === 'limited') + const isGitLab = integration.slug === 'gitlab' + const deploymentAvailable = availability?.state === 'ready' || availability?.state === 'limited' + const personalTokenAvailable = isGitLab && deploymentAvailable const { data: credentials = [], isPending: credentialsLoading } = useWorkspaceCredentials({ workspaceId, @@ -190,29 +190,29 @@ function IntegrationBlockDetailContent({ integration, workspaceId }: Integration * credential lists on every product it powers. Comparing resolved * `providerId`s instead hides it from all of them. */ - const connectedCredentials = useMemo(() => { - if (integration.type === 'gitlab') - return credentials.filter((c) => c.type === 'personal_token' && c.providerId === 'gitlab') - if (!oauthService) return [] - return credentials.filter( - (c) => - (c.type === 'oauth' || c.type === 'service_account') && - c.providerId && - credentialProviderMatchesService(c.providerId, oauthService) - ) - }, [credentials, oauthService, integration.type]) + const serviceCredentials = oauthService + ? credentials.filter( + (c) => + (c.type === 'oauth' || c.type === 'service_account') && + c.providerId && + credentialProviderMatchesService(c.providerId, oauthService) + ) + : [] + // GitLab also has personal tokens, which Copilot acts with on the person's behalf. + const connectedCredentials = isGitLab + ? [ + ...credentials.filter((c) => c.type === 'personal_token' && c.providerId === 'gitlab'), + ...serviceCredentials, + ] + : serviceCredentials const [serviceAccountOpen, setServiceAccountOpen] = useState(false) const serviceAccountTarget = useServiceAccountConnectTarget({ serviceAccountProviderId: oauthService?.serviceAccountProviderId, serviceName: oauthService?.serviceName, serviceIcon: oauthService?.serviceIcon, }) - const serviceAccountDeploymentAvailable = - availability?.state === 'ready' || availability?.state === 'limited' const hasServiceAccount = - serviceAccountDeploymentAvailable && - Boolean(serviceAccountTarget) && - !serviceAccountTarget?.hidden + deploymentAvailable && Boolean(serviceAccountTarget) && !serviceAccountTarget?.hidden const serviceAccountConnectLabel = serviceAccountTarget?.label ?? 'Add service account' const hasHandledConnectQueryRef = useRef(false) @@ -246,32 +246,42 @@ function IntegrationBlockDetailContent({ integration, workspaceId }: Integration setConnectMode, ]) - const connectOptions = oauthService - ? [ - ...(oauthAvailable - ? [ - { - value: CONNECT_MODE.oauth, - label: 'Connect with OAuth', - icon: oauthService.serviceIcon, - }, - ] - : []), - ...(hasServiceAccount - ? [ - { - value: CONNECT_MODE.serviceAccount, - label: serviceAccountConnectLabel, - icon: serviceAccountTarget?.serviceIcon ?? oauthService.serviceIcon, - }, - ] - : []), - ] - : [] + const connectOptions = [ + ...(personalTokenAvailable + ? [ + { + value: CONNECT_MODE.personalToken, + label: 'Add personal token', + icon: oauthService?.serviceIcon ?? Icon, + }, + ] + : []), + ...(oauthService && oauthAvailable + ? [ + { + value: CONNECT_MODE.oauth, + label: 'Connect with OAuth', + icon: oauthService.serviceIcon, + }, + ] + : []), + ...(oauthService && hasServiceAccount + ? [ + { + value: CONNECT_MODE.serviceAccount, + label: serviceAccountConnectLabel, + icon: serviceAccountTarget?.serviceIcon ?? oauthService.serviceIcon, + }, + ] + : []), + ] + + const singleConnectOption = connectOptions.length === 1 ? connectOptions[0] : undefined const handleSelectConnectOption = (value: string) => { if (value === CONNECT_MODE.oauth) setOAuthOpen(true) else if (value === CONNECT_MODE.serviceAccount) setServiceAccountOpen(true) + else if (value === CONNECT_MODE.personalToken) setPersonalTokenOpen(true) } const handleAddInChat = () => { @@ -286,32 +296,28 @@ function IntegrationBlockDetailContent({ integration, workspaceId }: Integration Integrations
- {personalTokenAvailable ? ( - setPersonalTokenOpen(true)}> - Add personal token + {connectOptions.length > 1 ? ( + + ) : singleConnectOption ? ( + handleSelectConnectOption(singleConnectOption.value)} + > + {singleConnectOption.value === CONNECT_MODE.oauth + ? 'Add to Sim' + : singleConnectOption.label} ) : oauthService ? ( - connectOptions.length > 1 ? ( - - ) : oauthAvailable ? ( - setOAuthOpen(true)}> - Add to Sim - - ) : hasServiceAccount ? ( - setServiceAccountOpen(true)}> - {serviceAccountConnectLabel} - - ) : ( - Unavailable - ) + Unavailable ) : chatEnabled ? ( Add to Sim diff --git a/apps/sim/blocks/blocks/ashby.ts b/apps/sim/blocks/blocks/ashby.ts index 1aaa985d031..7c3d6af7950 100644 --- a/apps/sim/blocks/blocks/ashby.ts +++ b/apps/sim/blocks/blocks/ashby.ts @@ -1,7 +1,7 @@ import { getErrorMessage } from '@sim/utils/errors' import { AshbyIcon } from '@/components/icons' import { AuthMode, type BlockConfig, type BlockMeta, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' function parseStringListInput(value: unknown): string[] { @@ -137,6 +137,8 @@ function parseJsonObjectInput(value: unknown, label: string): Record = { + ...commonParams, + question: params.question, + options: params.pollOptions, + } + if (params.isAnonymous !== undefined && params.isAnonymous !== '') { + pollParams.isAnonymous = toBoolean(params.isAnonymous) + } + if (params.allowsMultipleAnswers !== undefined && params.allowsMultipleAnswers !== '') { + pollParams.allowsMultipleAnswers = toBoolean(params.allowsMultipleAnswers) + } + return pollParams + } + case 'telegram_pin_message': { + const pinParams: Record = { + ...commonParams, + messageId: requireNumber(params.messageId, 'Message ID'), + } + if (params.disableNotification !== undefined && params.disableNotification !== '') { + pinParams.disableNotification = toBoolean(params.disableNotification) + } + return pinParams + } + case 'telegram_unpin_message': { + const unpinParams: Record = { ...commonParams } + if (params.messageId !== undefined && params.messageId !== '') { + unpinParams.messageId = requireNumber(params.messageId, 'Message ID') + } + return unpinParams + } + case 'telegram_set_message_reaction': { + const reactionParams: Record = { + ...commonParams, + messageId: requireNumber(params.messageId, 'Message ID'), + reaction: params.reactionEmoji, + } + if (params.isBig !== undefined && params.isBig !== '') { + reactionParams.isBig = toBoolean(params.isBig) + } + return reactionParams + } + case 'telegram_send_chat_action': + if (!params.action) { + throw new Error('Chat action is required.') + } + return { + ...commonParams, + action: params.action, + } + case 'telegram_get_chat': + return { ...commonParams } + case 'telegram_get_chat_member': + return { + ...commonParams, + userId: requireNumber(params.userId, 'User ID'), + } + default: + return { + ...commonParams, + text: params.text, + } + } +} + export const TelegramBlock: BlockConfig = { type: 'telegram', + hideFromToolbar: true, + sunset: { status: 'legacy', replacedBy: 'telegram_v2' }, name: 'Telegram', description: 'Interact with Telegram', authMode: AuthMode.BotToken, @@ -613,202 +812,7 @@ export const TelegramBlock: BlockConfig = { ], config: { tool: (params) => params.operation || 'telegram_message', - params: (params) => { - if (!params.botToken) throw new Error('Bot token required for this operation') - - const chatId = (params.chatId || '').trim() - if (!chatId) { - throw new Error('Chat ID is required.') - } - - const commonParams = { - botToken: params.botToken, - chatId, - } - - /** Coerce string/number input to a finite number, throwing with a labeled message. */ - const requireNumber = (value: unknown, label: string): number => { - const num = Number(value) - if (value === undefined || value === null || value === '' || Number.isNaN(num)) { - throw new Error(`${label} is required and must be a number.`) - } - return num - } - - /** Coerce agent-supplied "true"/"false" strings (or booleans) to a real boolean. */ - const toBoolean = (value: unknown): boolean => - value === true || String(value).toLowerCase() === 'true' - - switch (params.operation) { - case 'telegram_message': - if (!params.text) { - throw new Error('Message text is required.') - } - return { - ...commonParams, - text: params.text, - } - case 'telegram_delete_message': - return { - ...commonParams, - messageId: requireNumber(params.messageId, 'Message ID'), - } - case 'telegram_send_photo': { - return { - ...commonParams, - photo: resolveTelegramMedia(params.photoSource, params.photo, 'Photo'), - caption: params.caption, - } - } - case 'telegram_send_video': { - return { - ...commonParams, - video: resolveTelegramMedia(params.videoSource, params.video, 'Video'), - caption: params.caption, - } - } - case 'telegram_send_audio': { - return { - ...commonParams, - audio: resolveTelegramMedia(params.audioSource, params.audio, 'Audio'), - caption: params.caption, - } - } - case 'telegram_send_animation': { - return { - ...commonParams, - animation: resolveTelegramMedia( - params.animationSource, - params.animation, - 'Animation' - ), - caption: params.caption, - } - } - case 'telegram_send_document': { - // files is the canonical param for both basic (attachmentFiles) and advanced modes - return { - ...commonParams, - files: normalizeFileInput(params.files), - caption: params.caption, - } - } - case 'telegram_edit_message_text': - if (!params.text) { - throw new Error('Message text is required.') - } - return { - ...commonParams, - messageId: requireNumber(params.messageId, 'Message ID'), - text: params.text, - } - case 'telegram_forward_message': { - const fromChatId = (params.fromChatId || '').trim() - if (!fromChatId) { - throw new Error('Source chat ID is required.') - } - return { - ...commonParams, - fromChatId, - messageId: requireNumber(params.messageId, 'Message ID'), - } - } - case 'telegram_copy_message': { - const fromChatId = (params.fromChatId || '').trim() - if (!fromChatId) { - throw new Error('Source chat ID is required.') - } - return { - ...commonParams, - fromChatId, - messageId: requireNumber(params.messageId, 'Message ID'), - caption: params.caption, - } - } - case 'telegram_send_location': - return { - ...commonParams, - latitude: requireNumber(params.latitude, 'Latitude'), - longitude: requireNumber(params.longitude, 'Longitude'), - } - case 'telegram_send_contact': - if (!params.phoneNumber || !params.firstName) { - throw new Error('Phone number and first name are required.') - } - return { - ...commonParams, - phoneNumber: params.phoneNumber, - firstName: params.firstName, - lastName: params.lastName, - vcard: params.vcard, - } - case 'telegram_send_poll': { - if (!params.question) { - throw new Error('Poll question is required.') - } - const pollParams: Record = { - ...commonParams, - question: params.question, - options: params.pollOptions, - } - if (params.isAnonymous !== undefined && params.isAnonymous !== '') { - pollParams.isAnonymous = toBoolean(params.isAnonymous) - } - if (params.allowsMultipleAnswers !== undefined && params.allowsMultipleAnswers !== '') { - pollParams.allowsMultipleAnswers = toBoolean(params.allowsMultipleAnswers) - } - return pollParams - } - case 'telegram_pin_message': { - const pinParams: Record = { - ...commonParams, - messageId: requireNumber(params.messageId, 'Message ID'), - } - if (params.disableNotification !== undefined && params.disableNotification !== '') { - pinParams.disableNotification = toBoolean(params.disableNotification) - } - return pinParams - } - case 'telegram_unpin_message': { - const unpinParams: Record = { ...commonParams } - if (params.messageId !== undefined && params.messageId !== '') { - unpinParams.messageId = requireNumber(params.messageId, 'Message ID') - } - return unpinParams - } - case 'telegram_set_message_reaction': { - const reactionParams: Record = { - ...commonParams, - messageId: requireNumber(params.messageId, 'Message ID'), - reaction: params.reactionEmoji, - } - if (params.isBig !== undefined && params.isBig !== '') { - reactionParams.isBig = toBoolean(params.isBig) - } - return reactionParams - } - case 'telegram_send_chat_action': - if (!params.action) { - throw new Error('Chat action is required.') - } - return { - ...commonParams, - action: params.action, - } - case 'telegram_get_chat': - return { ...commonParams } - case 'telegram_get_chat_member': - return { - ...commonParams, - userId: requireNumber(params.userId, 'User ID'), - } - default: - return { - ...commonParams, - text: params.text, - } - } - }, + params: (params) => telegramToolParams(params, true), }, }, inputs: { @@ -1028,3 +1032,16 @@ export const TelegramBlockMeta = { }, ], } as const satisfies BlockMeta + +export const TelegramV2Block: BlockConfig = { + ...TelegramBlock, + type: 'telegram_v2', + authMode: AuthMode.OAuth, + hideFromToolbar: false, + sunset: undefined, + ...withSavedCredential( + TelegramBlock, + { id: 'credential', type: 'oauth-input', serviceId: 'telegram', title: 'Telegram Credential' }, + { params: (params) => telegramToolParams(params, false) } + ), +} diff --git a/apps/sim/blocks/blocks/typeform.ts b/apps/sim/blocks/blocks/typeform.ts index dbb1f1c4175..e112989f102 100644 --- a/apps/sim/blocks/blocks/typeform.ts +++ b/apps/sim/blocks/blocks/typeform.ts @@ -1,10 +1,13 @@ import { TypeformIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' export const TypeformBlock: BlockConfig = { type: 'typeform', + hideFromToolbar: true, + sunset: { status: 'legacy', replacedBy: 'typeform_v2' }, name: 'Typeform', description: 'Interact with Typeform', authMode: AuthMode.ApiKey, @@ -594,3 +597,19 @@ export const TypeformBlockMeta = { }, ], } as const satisfies BlockMeta + +export const TypeformV2Block: BlockConfig = { + ...TypeformBlock, + type: 'typeform_v2', + authMode: AuthMode.OAuth, + hideFromToolbar: false, + sunset: undefined, + longDescription: + 'Retrieve responses, download files, and get form insights. Use trigger mode to start a workflow when a form is submitted.', + ...withSavedCredential(TypeformBlock, { + id: 'credential', + type: 'oauth-input', + serviceId: 'typeform', + title: 'Typeform Credential', + }), +} diff --git a/apps/sim/blocks/blocks/vercel.ts b/apps/sim/blocks/blocks/vercel.ts index 6f4d6a43888..964b3e1fd6e 100644 --- a/apps/sim/blocks/blocks/vercel.ts +++ b/apps/sim/blocks/blocks/vercel.ts @@ -1,6 +1,7 @@ import { VercelIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' /** @@ -11,6 +12,8 @@ const DEPLOY_TARGET_PROJECT_FIELD = ['name', 'project'] as const export const VercelBlock: BlockConfig = { type: 'vercel', + hideFromToolbar: true, + sunset: { status: 'legacy', replacedBy: 'vercel_v2' }, name: 'Vercel', description: 'Manage Vercel deployments, projects, and infrastructure', longDescription: @@ -2349,3 +2352,17 @@ export const VercelBlockMeta = { }, ], } as const satisfies BlockMeta + +export const VercelV2Block: BlockConfig = { + ...VercelBlock, + type: 'vercel_v2', + authMode: AuthMode.OAuth, + hideFromToolbar: false, + sunset: undefined, + ...withSavedCredential(VercelBlock, { + id: 'credential', + type: 'oauth-input', + serviceId: 'vercel', + title: 'Vercel Credential', + }), +} diff --git a/apps/sim/blocks/blocks/whatsapp.ts b/apps/sim/blocks/blocks/whatsapp.ts index 7374f69ee36..d675ccfaaaa 100644 --- a/apps/sim/blocks/blocks/whatsapp.ts +++ b/apps/sim/blocks/blocks/whatsapp.ts @@ -1,7 +1,7 @@ import { WhatsAppIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' /** Yes/No dropdowns serialize as `'true'`/`'false'` strings; the tools expect booleans. */ @@ -19,6 +19,8 @@ const UPLOAD_FILE_FIELD = ['uploadFile', 'uploadFileRef'] as const export const WhatsAppBlock: BlockConfig = { type: 'whatsapp', + hideFromToolbar: true, + sunset: { status: 'legacy', replacedBy: 'whatsapp_v2' }, name: 'WhatsApp', description: 'Send WhatsApp messages', authMode: AuthMode.ApiKey, @@ -816,3 +818,17 @@ export const WhatsAppBlockMeta = { }, ], } as const satisfies BlockMeta + +export const WhatsAppV2Block: BlockConfig = { + ...WhatsAppBlock, + type: 'whatsapp_v2', + authMode: AuthMode.OAuth, + hideFromToolbar: false, + sunset: undefined, + ...withSavedCredential(WhatsAppBlock, { + id: 'credential', + type: 'oauth-input', + serviceId: 'whatsapp', + title: 'WhatsApp Credential', + }), +} diff --git a/apps/sim/blocks/legacy-builders.test.ts b/apps/sim/blocks/legacy-builders.test.ts index 3ec9d629197..4a14b0ffb03 100644 --- a/apps/sim/blocks/legacy-builders.test.ts +++ b/apps/sim/blocks/legacy-builders.test.ts @@ -5,6 +5,7 @@ import { Mem0Block } from '@/blocks/blocks/mem0' import { PulseV2Block } from '@/blocks/blocks/pulse' import { ReductoV2Block } from '@/blocks/blocks/reducto' import { S3Block } from '@/blocks/blocks/s3' +import { TelegramBlock } from '@/blocks/blocks/telegram' import { TextractV2Block } from '@/blocks/blocks/textract' import { ZepBlock } from '@/blocks/blocks/zep' import type { BlockConfig } from '@/blocks/types' @@ -74,6 +75,13 @@ const cases: Array<{ sent: { botToken: 'B' }, values: { operation: 'discord_send_message' }, }, + { + block: TelegramBlock, + missingSecretError: 'Bot token required for this operation', + secret: { botToken: 'B' }, + sent: { botToken: 'B' }, + values: { operation: 'telegram_message', chatId: 'chat', text: 'hi' }, + }, ...[ExtendV2Block, PulseV2Block, ReductoV2Block].map((block) => ({ block, missingSecretError: /trim/, diff --git a/apps/sim/blocks/registry-maps.ts b/apps/sim/blocks/registry-maps.ts index 81c9a644e15..96226548888 100644 --- a/apps/sim/blocks/registry-maps.ts +++ b/apps/sim/blocks/registry-maps.ts @@ -15,7 +15,7 @@ import { ApolloBlock, ApolloBlockMeta, ApolloV2Block } from '@/blocks/blocks/apo import { AppConfigBlock, AppConfigBlockMeta, AppConfigV2Block } from '@/blocks/blocks/appconfig' import { ArxivBlock, ArxivBlockMeta } from '@/blocks/blocks/arxiv' import { AsanaBlock, AsanaBlockMeta } from '@/blocks/blocks/asana' -import { AshbyBlock, AshbyBlockMeta } from '@/blocks/blocks/ashby' +import { AshbyBlock, AshbyBlockMeta, AshbyV2Block } from '@/blocks/blocks/ashby' import { AthenaBlock, AthenaBlockMeta, AthenaV2Block } from '@/blocks/blocks/athena' import { AttioBlock, AttioBlockMeta } from '@/blocks/blocks/attio' import { @@ -35,10 +35,10 @@ import { } from '@/blocks/blocks/browser_use' import { BufferBlock, BufferBlockMeta, BufferV2Block } from '@/blocks/blocks/buffer' import { CalComBlock, CalComBlockMeta } from '@/blocks/blocks/calcom' -import { CalendlyBlock, CalendlyBlockMeta } from '@/blocks/blocks/calendly' +import { CalendlyBlock, CalendlyBlockMeta, CalendlyV2Block } from '@/blocks/blocks/calendly' import { CbInsightsBlock, CbInsightsBlockMeta } from '@/blocks/blocks/cbinsights' import { ChatTriggerBlock } from '@/blocks/blocks/chat_trigger' -import { CheckrBlock, CheckrBlockMeta } from '@/blocks/blocks/checkr' +import { CheckrBlock, CheckrBlockMeta, CheckrV2Block } from '@/blocks/blocks/checkr' import { CirclebackBlock, CirclebackBlockMeta, CirclebackV2Block } from '@/blocks/blocks/circleback' import { ClayBlock, ClayBlockMeta } from '@/blocks/blocks/clay' import { ClerkBlock, ClerkBlockMeta, ClerkV2Block } from '@/blocks/blocks/clerk' @@ -95,7 +95,7 @@ import { EnrowBlock, EnrowBlockMeta } from '@/blocks/blocks/enrow' import { EvaluatorBlock } from '@/blocks/blocks/evaluator' import { ExaBlock, ExaBlockMeta } from '@/blocks/blocks/exa' import { ExtendBlock, ExtendBlockMeta, ExtendV2Block, ExtendV3Block } from '@/blocks/blocks/extend' -import { FathomBlock, FathomBlockMeta } from '@/blocks/blocks/fathom' +import { FathomBlock, FathomBlockMeta, FathomV2Block } from '@/blocks/blocks/fathom' import { FileBlock, FileV2Block, FileV3Block, FileV4Block, FileV5Block } from '@/blocks/blocks/file' import { FindymailBlock, FindymailBlockMeta } from '@/blocks/blocks/findymail' import { FirecrawlBlock, FirecrawlBlockMeta } from '@/blocks/blocks/firecrawl' @@ -119,7 +119,7 @@ import { GitHubV2Block, GitHubV2BlockMeta, } from '@/blocks/blocks/github' -import { GitLabBlock, GitLabBlockMeta } from '@/blocks/blocks/gitlab' +import { GitLabBlock, GitLabBlockMeta, GitLabV2Block } from '@/blocks/blocks/gitlab' import { GmailBlock, GmailBlockMeta, GmailV2Block, GmailV2BlockMeta } from '@/blocks/blocks/gmail' import { GongBlock, GongBlockMeta } from '@/blocks/blocks/gong' import { GoogleSearchBlock, GoogleSearchBlockMeta } from '@/blocks/blocks/google' @@ -157,8 +157,14 @@ import { GoogleTasksBlock, GoogleTasksBlockMeta } from '@/blocks/blocks/google_t import { GoogleTranslateBlock, GoogleTranslateBlockMeta } from '@/blocks/blocks/google_translate' import { GoogleVaultBlock, GoogleVaultBlockMeta } from '@/blocks/blocks/google_vault' import { GrafanaBlock, GrafanaBlockMeta } from '@/blocks/blocks/grafana' -import { GrainBlock, GrainBlockMeta, GrainV2Block, GrainV2BlockMeta } from '@/blocks/blocks/grain' -import { GranolaBlock, GranolaBlockMeta } from '@/blocks/blocks/granola' +import { + GrainBlock, + GrainBlockMeta, + GrainV2Block, + GrainV2BlockMeta, + GrainV3Block, +} from '@/blocks/blocks/grain' +import { GranolaBlock, GranolaBlockMeta, GranolaV2Block } from '@/blocks/blocks/granola' import { GreenhouseBlock, GreenhouseBlockMeta, @@ -227,7 +233,7 @@ import { LaunchDarklyV2Block, } from '@/blocks/blocks/launchdarkly' import { LeadMagicBlock, LeadMagicBlockMeta } from '@/blocks/blocks/leadmagic' -import { LemlistBlock, LemlistBlockMeta } from '@/blocks/blocks/lemlist' +import { LemlistBlock, LemlistBlockMeta, LemlistV2Block } from '@/blocks/blocks/lemlist' import { LinearBlock, LinearBlockMeta, LinearV2Block } from '@/blocks/blocks/linear' import { LinkedInBlock, LinkedInBlockMeta } from '@/blocks/blocks/linkedin' import { LinkupBlock, LinkupBlockMeta } from '@/blocks/blocks/linkup' @@ -301,7 +307,7 @@ import { import { OracleDatabaseBlock, OracleDatabaseBlockMeta } from '@/blocks/blocks/oracledb' import { OtterBlock, OtterBlockMeta, OtterV2Block } from '@/blocks/blocks/otter' import { OutlookBlock, OutlookBlockMeta } from '@/blocks/blocks/outlook' -import { PagerDutyBlock, PagerDutyBlockMeta } from '@/blocks/blocks/pagerduty' +import { PagerDutyBlock, PagerDutyBlockMeta, PagerDutyV2Block } from '@/blocks/blocks/pagerduty' import { ParallelBlock, ParallelBlockMeta } from '@/blocks/blocks/parallel' import { PeopleDataLabsBlock, PeopleDataLabsBlockMeta } from '@/blocks/blocks/peopledatalabs' import { PerplexityBlock, PerplexityBlockMeta } from '@/blocks/blocks/perplexity' @@ -336,12 +342,12 @@ import { ReductoV2Block, ReductoV3Block, } from '@/blocks/blocks/reducto' -import { ResendBlock, ResendBlockMeta } from '@/blocks/blocks/resend' +import { ResendBlock, ResendBlockMeta, ResendV2Block } from '@/blocks/blocks/resend' import { ResponseBlock } from '@/blocks/blocks/response' import { RevenueCatBlock, RevenueCatBlockMeta } from '@/blocks/blocks/revenuecat' import { RipplingBlock, RipplingBlockMeta, RipplingV2Block } from '@/blocks/blocks/rippling' import { RocketlaneBlock, RocketlaneBlockMeta, RocketlaneV2Block } from '@/blocks/blocks/rocketlane' -import { RootlyBlock, RootlyBlockMeta } from '@/blocks/blocks/rootly' +import { RootlyBlock, RootlyBlockMeta, RootlyV2Block } from '@/blocks/blocks/rootly' import { RouterBlock, RouterV2Block } from '@/blocks/blocks/router' import { RssBlock, RssBlockMeta } from '@/blocks/blocks/rss' import { S3Block, S3BlockMeta, S3V2Block } from '@/blocks/blocks/s3' @@ -359,7 +365,7 @@ import { import { SemrushBlock, SemrushBlockMeta } from '@/blocks/blocks/semrush' import { SendblueBlock, SendblueBlockMeta } from '@/blocks/blocks/sendblue' import { SendGridBlock, SendGridBlockMeta, SendGridV2Block } from '@/blocks/blocks/sendgrid' -import { SentryBlock, SentryBlockMeta } from '@/blocks/blocks/sentry' +import { SentryBlock, SentryBlockMeta, SentryV2Block } from '@/blocks/blocks/sentry' import { SerperBlock, SerperBlockMeta } from '@/blocks/blocks/serper' import { ServiceNowBlock, ServiceNowBlockMeta, ServiceNowV2Block } from '@/blocks/blocks/servicenow' import { SESBlock, SESBlockMeta, SESV2Block } from '@/blocks/blocks/ses' @@ -383,7 +389,7 @@ import { SSMBlock, SSMBlockMeta, SSMV2Block } from '@/blocks/blocks/ssm' import { StagehandBlock, StagehandBlockMeta } from '@/blocks/blocks/stagehand' import { StartTriggerBlock } from '@/blocks/blocks/start_trigger' import { StarterBlock } from '@/blocks/blocks/starter' -import { StripeBlock, StripeBlockMeta } from '@/blocks/blocks/stripe' +import { StripeBlock, StripeBlockMeta, StripeV2Block } from '@/blocks/blocks/stripe' import { STSBlock, STSBlockMeta, STSV2Block } from '@/blocks/blocks/sts' import { SttBlock, SttV2Block } from '@/blocks/blocks/stt' import { SupabaseBlock, SupabaseBlockMeta } from '@/blocks/blocks/supabase' @@ -391,7 +397,7 @@ import { TableBlock } from '@/blocks/blocks/table' import { TableV2Block } from '@/blocks/blocks/table_v2' import { TailscaleBlock, TailscaleBlockMeta, TailscaleV2Block } from '@/blocks/blocks/tailscale' import { TavilyBlock, TavilyBlockMeta, TavilyV2Block } from '@/blocks/blocks/tavily' -import { TelegramBlock, TelegramBlockMeta } from '@/blocks/blocks/telegram' +import { TelegramBlock, TelegramBlockMeta, TelegramV2Block } from '@/blocks/blocks/telegram' import { TemporalBlock, TemporalBlockMeta } from '@/blocks/blocks/temporal' import { TextractBlock, @@ -414,7 +420,7 @@ import { import { TtsBlock } from '@/blocks/blocks/tts' import { TwilioSMSBlock, TwilioSMSBlockMeta } from '@/blocks/blocks/twilio' import { TwilioVoiceBlock, TwilioVoiceBlockMeta } from '@/blocks/blocks/twilio_voice' -import { TypeformBlock, TypeformBlockMeta } from '@/blocks/blocks/typeform' +import { TypeformBlock, TypeformBlockMeta, TypeformV2Block } from '@/blocks/blocks/typeform' import { UpstashBlock, UpstashBlockMeta } from '@/blocks/blocks/upstash' import { UptimeRobotBlock, @@ -423,7 +429,7 @@ import { } from '@/blocks/blocks/uptimerobot' import { VantaBlock, VantaBlockMeta } from '@/blocks/blocks/vanta' import { VariablesBlock } from '@/blocks/blocks/variables' -import { VercelBlock, VercelBlockMeta } from '@/blocks/blocks/vercel' +import { VercelBlock, VercelBlockMeta, VercelV2Block } from '@/blocks/blocks/vercel' import { VideoGeneratorBlock, VideoGeneratorV2Block, @@ -434,7 +440,7 @@ import { WaitBlock } from '@/blocks/blocks/wait' import { WealthboxBlock, WealthboxBlockMeta } from '@/blocks/blocks/wealthbox' import { WebflowBlock, WebflowBlockMeta } from '@/blocks/blocks/webflow' import { WebhookRequestBlock } from '@/blocks/blocks/webhook_request' -import { WhatsAppBlock, WhatsAppBlockMeta } from '@/blocks/blocks/whatsapp' +import { WhatsAppBlock, WhatsAppBlockMeta, WhatsAppV2Block } from '@/blocks/blocks/whatsapp' import { WikipediaBlock, WikipediaBlockMeta } from '@/blocks/blocks/wikipedia' import { WindchillBlock, WindchillBlockMeta } from '@/blocks/blocks/windchill' import { WizaBlock, WizaBlockMeta } from '@/blocks/blocks/wiza' @@ -480,6 +486,7 @@ export const BLOCK_REGISTRY: Record = { arxiv: ArxivBlock, asana: AsanaBlock, ashby: AshbyBlock, + ashby_v2: AshbyV2Block, athena: AthenaBlock, athena_v2: AthenaV2Block, attio: AttioBlock, @@ -499,9 +506,11 @@ export const BLOCK_REGISTRY: Record = { buffer_v2: BufferV2Block, calcom: CalComBlock, calendly: CalendlyBlock, + calendly_v2: CalendlyV2Block, cbinsights: CbInsightsBlock, chat_trigger: ChatTriggerBlock, checkr: CheckrBlock, + checkr_v2: CheckrV2Block, circleback: CirclebackBlock, circleback_v2: CirclebackV2Block, clay: ClayBlock, @@ -574,6 +583,7 @@ export const BLOCK_REGISTRY: Record = { extend_v2: ExtendV2Block, extend_v3: ExtendV3Block, fathom: FathomBlock, + fathom_v2: FathomV2Block, file: FileBlock, file_v2: FileV2Block, file_v3: FileV3Block, @@ -599,6 +609,7 @@ export const BLOCK_REGISTRY: Record = { github: GitHubBlock, github_v2: GitHubV2Block, gitlab: GitLabBlock, + gitlab_v2: GitLabV2Block, gmail: GmailBlock, gmail_v2: GmailV2Block, gong: GongBlock, @@ -627,7 +638,9 @@ export const BLOCK_REGISTRY: Record = { grafana: GrafanaBlock, grain: GrainBlock, grain_v2: GrainV2Block, + grain_v3: GrainV3Block, granola: GranolaBlock, + granola_v2: GranolaV2Block, greenhouse: GreenhouseBlock, greenhouse_v2: GreenhouseV2Block, greptile: GreptileBlock, @@ -682,6 +695,7 @@ export const BLOCK_REGISTRY: Record = { launchdarkly_v2: LaunchDarklyV2Block, leadmagic: LeadMagicBlock, lemlist: LemlistBlock, + lemlist_v2: LemlistV2Block, linear: LinearBlock, linear_v2: LinearV2Block, linkedin: LinkedInBlock, @@ -742,6 +756,7 @@ export const BLOCK_REGISTRY: Record = { otter_v2: OtterV2Block, outlook: OutlookBlock, pagerduty: PagerDutyBlock, + pagerduty_v2: PagerDutyV2Block, parallel_ai: ParallelBlock, peopledatalabs: PeopleDataLabsBlock, perplexity: PerplexityBlock, @@ -783,6 +798,7 @@ export const BLOCK_REGISTRY: Record = { reducto_v2: ReductoV2Block, reducto_v3: ReductoV3Block, resend: ResendBlock, + resend_v2: ResendV2Block, response: ResponseBlock, revenuecat: RevenueCatBlock, rippling: RipplingBlock, @@ -790,6 +806,7 @@ export const BLOCK_REGISTRY: Record = { rocketlane: RocketlaneBlock, rocketlane_v2: RocketlaneV2Block, rootly: RootlyBlock, + rootly_v2: RootlyV2Block, router: RouterBlock, router_v2: RouterV2Block, rss: RssBlock, @@ -808,6 +825,7 @@ export const BLOCK_REGISTRY: Record = { sendgrid: SendGridBlock, sendgrid_v2: SendGridV2Block, sentry: SentryBlock, + sentry_v2: SentryV2Block, serper: SerperBlock, servicenow: ServiceNowBlock, servicenow_v2: ServiceNowV2Block, @@ -845,6 +863,7 @@ export const BLOCK_REGISTRY: Record = { start_trigger: StartTriggerBlock, starter: StarterBlock, stripe: StripeBlock, + stripe_v2: StripeV2Block, sts: STSBlock, sts_v2: STSV2Block, stt: SttBlock, @@ -857,6 +876,7 @@ export const BLOCK_REGISTRY: Record = { tavily: TavilyBlock, tavily_v2: TavilyV2Block, telegram: TelegramBlock, + telegram_v2: TelegramV2Block, temporal: TemporalBlock, textract: TextractBlock, textract_v2: TextractV2Block, @@ -874,12 +894,14 @@ export const BLOCK_REGISTRY: Record = { twilio_sms: TwilioSMSBlock, twilio_voice: TwilioVoiceBlock, typeform: TypeformBlock, + typeform_v2: TypeformV2Block, upstash: UpstashBlock, uptimerobot: UptimeRobotBlock, uptimerobot_v2: UptimeRobotV2Block, vanta: VantaBlock, variables: VariablesBlock, vercel: VercelBlock, + vercel_v2: VercelV2Block, video_generator: VideoGeneratorBlock, video_generator_v2: VideoGeneratorV2Block, video_generator_v3: VideoGeneratorV3Block, @@ -890,6 +912,7 @@ export const BLOCK_REGISTRY: Record = { webflow: WebflowBlock, webhook_request: WebhookRequestBlock, whatsapp: WhatsAppBlock, + whatsapp_v2: WhatsAppV2Block, wikipedia: WikipediaBlock, windchill: WindchillBlock, wiza: WizaBlock, diff --git a/apps/sim/blocks/utils.test.ts b/apps/sim/blocks/utils.test.ts index 37daa535330..960b11834bb 100644 --- a/apps/sim/blocks/utils.test.ts +++ b/apps/sim/blocks/utils.test.ts @@ -292,6 +292,50 @@ describe('withSavedCredential', () => { outputs: {}, } + it('places the picker right under the operation picker, ahead of the other fields', () => { + const { subBlocks } = withSavedCredential( + { + ...legacy, + subBlocks: [ + { id: 'operation', title: 'Operation', type: 'dropdown', options: [] }, + { id: 'bucket', title: 'Bucket', type: 'short-input' }, + { id: 'accessKeyId', title: 'Access Key ID', type: 'short-input', required: true }, + { id: 'secretAccessKey', title: 'Secret', type: 'short-input', required: true }, + ], + }, + picker + ) + + expect(subBlocks.map((subBlock) => subBlock.id)).toEqual([ + 'operation', + 'credential', + 'manualCredential', + 'bucket', + ]) + }) + + it('keeps the picker where the secret was when the block has no operation picker', () => { + const { subBlocks } = withSavedCredential( + { + ...legacy, + subBlocks: [ + { id: 'region', title: 'Region', type: 'short-input' }, + { id: 'accessKeyId', title: 'Access Key ID', type: 'short-input', required: true }, + { id: 'secretAccessKey', title: 'Secret', type: 'short-input', required: true }, + { id: 'bucket', title: 'Bucket', type: 'short-input' }, + ], + }, + picker + ) + + expect(subBlocks.map((subBlock) => subBlock.id)).toEqual([ + 'region', + 'credential', + 'manualCredential', + 'bucket', + ]) + }) + it('keeps trigger fields that share a replaced secret id, untouched', () => { const { subBlocks } = withSavedCredential(legacy, picker) const triggerFields = legacy.subBlocks.filter((subBlock) => subBlock.mode === 'trigger') @@ -386,6 +430,19 @@ describe('withSavedCredential', () => { expect(() => withSavedCredential(mismatched, picker)).toThrow(/show or require differently/) }) + it('accepts replaced fields that differ only in what the successor states itself', () => { + const optionalHost: BlockConfig = { + ...legacy, + subBlocks: legacy.subBlocks.map((subBlock) => + subBlock.id === 'secretAccessKey' ? { ...subBlock, required: false } : subBlock + ), + } + + const { subBlocks } = withSavedCredential(optionalHost, picker, { required: true }) + + expect(subBlocks.find((subBlock) => subBlock.id === 'credential')?.required).toBe(true) + }) + it('refuses options that would silently do nothing', () => { expect(() => withSavedCredential(legacy, picker, { diff --git a/apps/sim/blocks/utils.ts b/apps/sim/blocks/utils.ts index 200934cd657..4b3c26de00b 100644 --- a/apps/sim/blocks/utils.ts +++ b/apps/sim/blocks/utils.ts @@ -83,7 +83,8 @@ interface SavedCredentialOptions { * The fields of a credential-only successor to a block that takes inline * secrets. The legacy block keeps its own definition and keeps running; the * successor spreads it, then these fields swap its secret fields for a - * saved-credential picker in the same place. Fields that depended on + * saved-credential picker right under the operation picker (or where the + * secret was, when the block has none). Fields that depended on * a replaced secret depend on the credential instead. */ export function withSavedCredential( @@ -104,11 +105,15 @@ export function withSavedCredential( throw new Error(`Block "${legacy.type}" has none of the fields a saved credential replaces`) } - // The picker shows and is required exactly where the secrets it replaces were. + // The picker shows and is required exactly where the secrets it replaces were, + // unless the successor states where; replaced fields must agree on whatever it inherits. const { condition: secretCondition, required: secretRequired } = legacy.subBlocks[at] - const visibility = (subBlock: SubBlockConfig) => - JSON.stringify([subBlock.condition ?? null, subBlock.required ?? null]) - if (new Set(legacy.subBlocks.filter(isReplaced).map(visibility)).size > 1) { + const inherited = (subBlock: SubBlockConfig) => + JSON.stringify([ + options.condition === undefined ? (subBlock.condition ?? null) : null, + options.required === undefined ? (subBlock.required ?? null) : null, + ]) + if (new Set(legacy.subBlocks.filter(isReplaced).map(inherited)).size > 1) { throw new Error(`Block "${legacy.type}": the replaced secrets show or require differently`) } const condition = options.condition ?? secretCondition @@ -142,8 +147,12 @@ export function withSavedCredential( `Block "${legacy.type}": unused selector twins ${unusedSelectorKeys.join(', ')}` ) } + // Under the operation picker because that is the first field a user sets. + const operationAt = subBlocks.findIndex( + (subBlock) => subBlock.id === 'operation' && !isTriggerModeSubBlock(subBlock) + ) subBlocks.splice( - at, + operationAt === -1 ? at : operationAt + 1, 0, { ...picker, diff --git a/apps/sim/lib/block-metadata/names.generated.ts b/apps/sim/lib/block-metadata/names.generated.ts index e80dcdff9e1..c82d606b79a 100644 --- a/apps/sim/lib/block-metadata/names.generated.ts +++ b/apps/sim/lib/block-metadata/names.generated.ts @@ -19,7 +19,7 @@ export const BLOCK_NAMES: Readonly> = { appconfig_v2: 'AWS AppConfig', arxiv: 'ArXiv', asana: 'Asana', - ashby: 'Ashby', + ashby_v2: 'Ashby', athena_v2: 'Athena', attio: 'Attio', azure_data_explorer: 'Azure Data Explorer', @@ -32,9 +32,9 @@ export const BLOCK_NAMES: Readonly> = { browser_use_v2: 'Browser Use', buffer_v2: 'Buffer', calcom: 'Cal.com', - calendly: 'Calendly', + calendly_v2: 'Calendly', cbinsights: 'CB Insights', - checkr: 'Checkr', + checkr_v2: 'Checkr', circleback_v2: 'Circleback', clay: 'Clay', clerk_v2: 'Clerk', @@ -83,7 +83,7 @@ export const BLOCK_NAMES: Readonly> = { evaluator: 'Evaluator', exa: 'Exa', extend_v3: 'Extend', - fathom: 'Fathom', + fathom_v2: 'Fathom', file_v5: 'File', findymail: 'Findymail', firecrawl: 'Firecrawl', @@ -96,7 +96,7 @@ export const BLOCK_NAMES: Readonly> = { gauge: 'Gauge', generic_webhook: 'Webhook Trigger', github_v2: 'GitHub', - gitlab: 'GitLab', + gitlab_v2: 'GitLab', gmail_v2: 'Gmail', gong: 'Gong', google_ads: 'Google Ads', @@ -119,8 +119,8 @@ export const BLOCK_NAMES: Readonly> = { google_translate: 'Google Translate', google_vault: 'Google Vault', grafana: 'Grafana', - grain_v2: 'Grain', - granola: 'Granola', + grain_v3: 'Grain', + granola_v2: 'Granola', greenhouse_v2: 'Greenhouse', greptile: 'Greptile', guardrails: 'Guardrails', @@ -157,7 +157,7 @@ export const BLOCK_NAMES: Readonly> = { latex: 'LaTeX', launchdarkly_v2: 'LaunchDarkly', leadmagic: 'LeadMagic', - lemlist: 'Lemlist', + lemlist_v2: 'Lemlist', linear_v2: 'Linear', linkedin: 'LinkedIn', linkup: 'Linkup', @@ -205,7 +205,7 @@ export const BLOCK_NAMES: Readonly> = { oracledb: 'Oracle Database', otter_v2: 'Otter.ai', outlook: 'Outlook', - pagerduty: 'PagerDuty', + pagerduty_v2: 'PagerDuty', parallel: 'Parallel', parallel_ai: 'Parallel AI', peopledatalabs: 'People Data Labs', @@ -236,12 +236,12 @@ export const BLOCK_NAMES: Readonly> = { reddit: 'Reddit', redis: 'Redis', reducto_v3: 'Reducto', - resend: 'Resend', + resend_v2: 'Resend', response: 'Response', revenuecat: 'RevenueCat', rippling_v2: 'Rippling', rocketlane_v2: 'Rocketlane', - rootly: 'Rootly', + rootly_v2: 'Rootly', router_v2: 'Router', rss: 'RSS Feed', s3_v2: 'S3', @@ -255,7 +255,7 @@ export const BLOCK_NAMES: Readonly> = { semrush: 'Semrush', sendblue: 'Sendblue', sendgrid_v2: 'SendGrid', - sentry: 'Sentry', + sentry_v2: 'Sentry', serper: 'Serper', servicenow_v2: 'ServiceNow', ses_v2: 'AWS SES', @@ -278,14 +278,14 @@ export const BLOCK_NAMES: Readonly> = { ssm_v2: 'AWS Systems Manager', stagehand: 'Stagehand', start_trigger: 'Start', - stripe: 'Stripe', + stripe_v2: 'Stripe', sts_v2: 'AWS STS', stt_v2: 'Speech-to-Text', supabase: 'Supabase', table_v2: 'Table', tailscale_v2: 'Tailscale', tavily_v2: 'Tavily', - telegram: 'Telegram', + telegram_v2: 'Telegram', temporal: 'Temporal', textract_v3: 'AWS Textract', thinking: 'Thinking', @@ -299,12 +299,12 @@ export const BLOCK_NAMES: Readonly> = { tts: 'Text-to-Speech', twilio_sms: 'Twilio SMS', twilio_voice: 'Twilio Voice', - typeform: 'Typeform', + typeform_v2: 'Typeform', upstash: 'Upstash', uptimerobot_v2: 'UptimeRobot', vanta: 'Vanta', variables: 'Variables', - vercel: 'Vercel', + vercel_v2: 'Vercel', video_generator_v3: 'Video Generator', vision: 'Vision (Legacy)', vision_v2: 'Vision', @@ -312,7 +312,7 @@ export const BLOCK_NAMES: Readonly> = { wealthbox: 'Wealthbox', webflow: 'Webflow', webhook_request: 'Webhook', - whatsapp: 'WhatsApp', + whatsapp_v2: 'WhatsApp', wikipedia: 'Wikipedia', windchill: 'Windchill', wiza: 'Wiza', diff --git a/apps/sim/lib/credentials/databricks-service-account.server.test.ts b/apps/sim/lib/credentials/databricks-service-account.server.test.ts index fc276c9b92c..afbb90ed2a2 100644 --- a/apps/sim/lib/credentials/databricks-service-account.server.test.ts +++ b/apps/sim/lib/credentials/databricks-service-account.server.test.ts @@ -2,13 +2,22 @@ * @vitest-environment node */ +import { queueTableRows, resetDbChainMock } from '@sim/testing' import { jsonResponse } from '@sim/testing/helpers/http' import { encryptionMock, encryptionMockFns } from '@sim/testing/mocks/encryption.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/core/security/encryption', () => encryptionMock) +vi.mock('@/lib/credentials/workload-identity/issuer', () => ({ + mintWorkloadIdentityToken: async ({ subject }: { subject: string }) => `token-for:${subject}`, +})) -import { verifyAndEncryptDatabricksCredential } from '@/lib/credentials/databricks-service-account.server' +import { credential } from '@sim/db/schema' +import { + resolveDatabricksCredentialToken, + verifyAndEncryptDatabricksCredential, +} from '@/lib/credentials/databricks-service-account.server' +import { DATABRICKS_SERVICE_ACCOUNT_PROVIDER_ID } from '@/lib/oauth/types' const owner = { workspaceId: 'ws-1', actorUserId: 'user-1' } @@ -49,3 +58,127 @@ describe('saving a Databricks connection', () => { expect(encryptionMockFns.mockEncryptSecret).not.toHaveBeenCalled() }) }) + +describe('saving a keyless Databricks connection', () => { + const HOST = 'dbc-1234abcd-5678.cloud.databricks.com' + const CLIENT_ID = '00000000-0000-0000-0000-000000000000' + const SUBJECT = 'sim:workspace:ws-1:credential:abcdef0123456789' + const fetch = vi.fn() + const config = { authType: 'oidc_federation', host: HOST, clientId: CLIENT_ID } + const reconnectOwner = { + ...owner, + storedSecret: { type: 'databricks_credential_v1', config: { ...config, subject: SUBJECT } }, + } + + /** A service principal whose federation policy pins `subject` to {@link SUBJECT}, or trusts any subject. */ + function principalTrusting(pinsSubject: boolean, probeStatus = 401) { + fetch.mockImplementation(async (url: string, init: RequestInit) => { + if (url.endsWith('/oidc/v1/token')) { + const body = new URLSearchParams(String(init.body)) + const trusted = !pinsSubject || body.get('subject_token') === `token-for:${SUBJECT}` + return trusted + ? jsonResponse({ access_token: 'dbx-token' }) + : jsonResponse({ error: 'invalid_grant' }, probeStatus) + } + return jsonResponse({ userName: 'sim-workflows' }) + }) + } + + beforeEach(() => { + fetch.mockReset() + vi.stubGlobal('fetch', fetch) + encryptionMockFns.mockEncryptSecret.mockReset() + encryptionMockFns.mockEncryptSecret.mockResolvedValue({ encrypted: 'ciphertext', iv: 'iv' }) + }) + + it('exchanges this credential’s token as the service principal and saves', async () => { + principalTrusting(true) + + await expect( + verifyAndEncryptDatabricksCredential(JSON.stringify(config), reconnectOwner) + ).resolves.toMatchObject({ identity: 'sim-workflows' }) + const exchange = fetch.mock.calls + .map(([url, init]) => ({ url, body: new URLSearchParams(String(init?.body)) })) + .find(({ body }) => body.get('subject_token') === `token-for:${SUBJECT}`) + expect(exchange?.url).toBe(`https://${HOST}/oidc/v1/token`) + expect(Object.fromEntries(exchange?.body ?? [])).toEqual({ + client_id: CLIENT_ID, + subject_token: `token-for:${SUBJECT}`, + subject_token_type: 'urn:ietf:params:oauth:token-type:jwt', + grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', + scope: 'all-apis', + }) + }) + + it('refuses a federation policy that trusts any Sim subject, so no other workspace can use it', async () => { + principalTrusting(false) + + await expect( + verifyAndEncryptDatabricksCredential(JSON.stringify(config), reconnectOwner) + ).rejects.toThrow(/also trusts other Sim credentials/) + expect(encryptionMockFns.mockEncryptSecret).not.toHaveBeenCalled() + }) + + it('does not save when the any-subject check cannot be completed', async () => { + principalTrusting(true, 503) + + await expect( + verifyAndEncryptDatabricksCredential(JSON.stringify(config), reconnectOwner) + ).rejects.toThrow(/could not confirm/) + expect(encryptionMockFns.mockEncryptSecret).not.toHaveBeenCalled() + }) +}) + +describe('getting a Databricks token for a run', () => { + const fetch = vi.fn() + + beforeEach(() => { + resetDbChainMock() + queueTableRows(credential, [ + { + workspaceId: 'ws-1', + organizationId: null, + type: 'service_account', + providerId: DATABRICKS_SERVICE_ACCOUNT_PROVIDER_ID, + revokedAt: null, + encryptedServiceAccountKey: 'ciphertext', + }, + ]) + encryptionMockFns.mockDecryptSecret.mockResolvedValueOnce({ + decrypted: JSON.stringify({ + type: 'databricks_credential_v1', + config: { + authType: 'service_principal', + host: 'dbc-1234abcd-5678.cloud.databricks.com', + clientId: '00000000-0000-0000-0000-000000000000', + clientSecret: 'dose-secret', + }, + }), + }) + vi.stubGlobal('fetch', fetch) + }) + + it('reports a refused client secret as a credential to reconnect', async () => { + fetch.mockResolvedValue(jsonResponse({ error: 'invalid_client' }, 401)) + + await expect(resolveDatabricksCredentialToken('cred-1')).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) + + it('reports a Databricks outage as the provider being unavailable, not a bad credential', async () => { + fetch.mockResolvedValue(jsonResponse({ error: 'temporarily_unavailable' }, 503)) + + await expect(resolveDatabricksCredentialToken('cred-1')).rejects.toMatchObject({ + code: 'provider_unavailable', + }) + }) + + it('reports a token reply without a token as the provider misbehaving, not a bad credential', async () => { + fetch.mockResolvedValue(jsonResponse({}, 200)) + + await expect(resolveDatabricksCredentialToken('cred-1')).rejects.toMatchObject({ + code: 'provider_unavailable', + }) + }) +}) diff --git a/apps/sim/lib/credentials/databricks-service-account.server.ts b/apps/sim/lib/credentials/databricks-service-account.server.ts index a4cd3cc9510..fa94093a9be 100644 --- a/apps/sim/lib/credentials/databricks-service-account.server.ts +++ b/apps/sim/lib/credentials/databricks-service-account.server.ts @@ -6,6 +6,10 @@ import { z } from 'zod' import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' import { validateDatabricksWorkspaceHost } from '@/lib/core/security/input-validation' import { parseServiceAccountConfig } from '@/lib/credentials/service-account-config' +import { + providerFailureCode, + TokenServiceAccountValidationError, +} from '@/lib/credentials/token-service-accounts/errors' import { WORKLOAD_IDENTITY_AUDIENCES } from '@/lib/credentials/workload-identity/audiences' import { mintWorkloadIdentityToken, @@ -13,6 +17,7 @@ import { } from '@/lib/credentials/workload-identity/issuer' import { bindWorkloadIdentitySubject, + newWorkloadIdentitySubject, type WorkloadIdentityCredentialOwner, WorkloadIdentitySubjectError, } from '@/lib/credentials/workload-identity/subject' @@ -112,7 +117,11 @@ const storedSecretSchema = z .strict() export class DatabricksCredentialError extends Error { - constructor(message: string) { + /** @param status Databricks' HTTP status, when Databricks itself refused the request. */ + constructor( + message: string, + readonly status?: number + ) { super(message) this.name = 'DatabricksCredentialError' } @@ -145,7 +154,10 @@ async function requestWorkspaceToken( : typeof payload.error === 'string' ? payload.error : `HTTP ${response.status}` - throw new DatabricksCredentialError(`Databricks rejected the token request: ${reason}`) + throw new DatabricksCredentialError( + `Databricks rejected the token request: ${reason}`, + response.status + ) } return payload.access_token } @@ -212,6 +224,67 @@ function bindSubject( } } +/** Proves a configuration by calling the workspace's SCIM `Me` with the resolved token. */ +async function verifyWorkspaceIdentity( + config: DatabricksCredentialConfig, + owner: WorkloadIdentityOwner +): Promise { + try { + const token = await resolveDatabricksAccessToken(config, owner) + const response = await fetch(`${workspaceOrigin(config.host)}/api/2.0/preview/scim/v2/Me`, { + headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, + redirect: 'error', + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), + }) + if (!response.ok) throw new Error(`workspace returned HTTP ${response.status}`) + const me = (await response.json().catch(() => ({}))) as { + userName?: unknown + displayName?: unknown + } + return ( + (typeof me.userName === 'string' && me.userName) || + (typeof me.displayName === 'string' && me.displayName) || + config.host + ) + } catch (error) { + throw new DatabricksCredentialError( + `Could not verify the Databricks credential: ${getErrorMessage(error, 'Unknown error')}` + ) + } +} + +/** + * Sim signs tokens for any service principal a member names, and every Sim + * tenant shares the issuer and audience, so a federation policy that matches on + * a claim other than `sub` (a `subject_claim` of `sim_workspace_id`, say) would + * let other Sim credentials act as the principal. Exchanging a subject no + * credential holds must be refused before the credential is saved. + */ +async function assertPolicyPinsSubject( + config: Extract, + owner: WorkloadIdentityOwner +): Promise { + try { + await resolveDatabricksAccessToken( + { ...config, subject: newWorkloadIdentitySubject(owner) }, + owner, + AbortSignal.timeout(REQUEST_TIMEOUT_MS) + ) + } catch (error) { + const refused = + error instanceof DatabricksCredentialError && + error.status !== undefined && + providerFailureCode(error.status) === 'invalid_credentials' + if (refused) return + throw new DatabricksCredentialError( + `Sim could not confirm the federation policy limits this service principal to this credential: ${getErrorMessage(error, 'Unknown error')}. Try again.` + ) + } + throw new DatabricksCredentialError( + `This service principal's federation policy also trusts other Sim credentials. Use the policy shown in the dialog, which matches the sub claim to ${config.subject}, so only this credential can use it.` + ) +} + /** * Validates the submitted configuration, proves it by calling the workspace's * SCIM `Me` endpoint with the resolved token, and returns the encrypted blob @@ -236,28 +309,15 @@ export async function verifyAndEncryptDatabricksCredential( config = submitted } - let identity: string - try { - const token = await resolveDatabricksAccessToken(config, owner) - const response = await fetch(`${workspaceOrigin(config.host)}/api/2.0/preview/scim/v2/Me`, { - headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, - redirect: 'error', - signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), - }) - if (!response.ok) throw new Error(`workspace returned HTTP ${response.status}`) - const me = (await response.json().catch(() => ({}))) as { - userName?: unknown - displayName?: unknown - } - identity = - (typeof me.userName === 'string' && me.userName) || - (typeof me.displayName === 'string' && me.displayName) || - config.host - } catch (error) { - throw new DatabricksCredentialError( - `Could not verify the Databricks credential: ${getErrorMessage(error, 'Unknown error')}` - ) - } + // The subject-pin probe is independent of the verification, so both run at + // once; a verification failure still reports first. + const [verified, pinned] = await Promise.allSettled([ + verifyWorkspaceIdentity(config, owner), + config.authType === 'oidc_federation' ? assertPolicyPinsSubject(config, owner) : undefined, + ]) + if (verified.status === 'rejected') throw verified.reason + if (pinned.status === 'rejected') throw pinned.reason + const identity = verified.value const { encrypted } = await encryptSecret( JSON.stringify({ type: DATABRICKS_CREDENTIAL_SECRET_TYPE, config }) @@ -299,8 +359,18 @@ export async function resolveDatabricksCredentialToken( const owner: WorkloadIdentityOwner = row.workspaceId ? { workspaceId: row.workspaceId } : { organizationId: row.organizationId ?? undefined } - return { - accessToken: await resolveDatabricksAccessToken(config, owner, signal), - domain: config.host, + try { + return { + accessToken: await resolveDatabricksAccessToken(config, owner, signal), + domain: config.host, + } + } catch (error) { + if (!(error instanceof DatabricksCredentialError)) throw error + // Report in the shared service-account codes: an outage is not a bad credential. + throw new TokenServiceAccountValidationError( + error.status === undefined ? 'invalid_credentials' : providerFailureCode(error.status), + error.status ?? 400, + { step: 'token', reason: error.message } + ) } } diff --git a/apps/sim/lib/credentials/token-service-accounts/descriptors.ts b/apps/sim/lib/credentials/token-service-accounts/descriptors.ts index 69a578099ed..3eeeaf02a2f 100644 --- a/apps/sim/lib/credentials/token-service-accounts/descriptors.ts +++ b/apps/sim/lib/credentials/token-service-accounts/descriptors.ts @@ -85,6 +85,23 @@ export const FLOW_SERVICE_ACCOUNT_PROVIDER_ID = 'flow-service-account' as const export const CODA_SERVICE_ACCOUNT_PROVIDER_ID = 'coda-service-account' as const export const GAUGE_SERVICE_ACCOUNT_PROVIDER_ID = 'gauge-service-account' as const export const HIGGSFIELD_SERVICE_ACCOUNT_PROVIDER_ID = 'higgsfield-service-account' as const +export const GITLAB_SERVICE_ACCOUNT_PROVIDER_ID = 'gitlab-service-account' as const +export const WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID = 'whatsapp-service-account' as const +export const VERCEL_SERVICE_ACCOUNT_PROVIDER_ID = 'vercel-service-account' as const +export const TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID = 'typeform-service-account' as const +export const TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID = 'telegram-service-account' as const +export const STRIPE_SERVICE_ACCOUNT_PROVIDER_ID = 'stripe-service-account' as const +export const SENTRY_SERVICE_ACCOUNT_PROVIDER_ID = 'sentry-service-account' as const +export const ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID = 'rootly-service-account' as const +export const RESEND_SERVICE_ACCOUNT_PROVIDER_ID = 'resend-service-account' as const +export const PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID = 'pagerduty-service-account' as const +export const LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID = 'lemlist-service-account' as const +export const GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID = 'granola-service-account' as const +export const GRAIN_SERVICE_ACCOUNT_PROVIDER_ID = 'grain-service-account' as const +export const FATHOM_SERVICE_ACCOUNT_PROVIDER_ID = 'fathom-service-account' as const +export const CHECKR_SERVICE_ACCOUNT_PROVIDER_ID = 'checkr-service-account' as const +export const CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID = 'calendly-service-account' as const +export const ASHBY_SERVICE_ACCOUNT_PROVIDER_ID = 'ashby-service-account' as const export const FLINT_SERVICE_ACCOUNT_PROVIDER_ID = 'flint-service-account' as const export const REDUCTO_SERVICE_ACCOUNT_PROVIDER_ID = 'reducto-service-account' as const export const QUIVER_SERVICE_ACCOUNT_PROVIDER_ID = 'quiver-service-account' as const @@ -175,6 +192,23 @@ export type TokenServiceAccountProviderId = | typeof CODA_SERVICE_ACCOUNT_PROVIDER_ID | typeof GAUGE_SERVICE_ACCOUNT_PROVIDER_ID | typeof HIGGSFIELD_SERVICE_ACCOUNT_PROVIDER_ID + | typeof GITLAB_SERVICE_ACCOUNT_PROVIDER_ID + | typeof WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID + | typeof VERCEL_SERVICE_ACCOUNT_PROVIDER_ID + | typeof TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID + | typeof TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID + | typeof STRIPE_SERVICE_ACCOUNT_PROVIDER_ID + | typeof SENTRY_SERVICE_ACCOUNT_PROVIDER_ID + | typeof ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID + | typeof RESEND_SERVICE_ACCOUNT_PROVIDER_ID + | typeof PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID + | typeof LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID + | typeof GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID + | typeof GRAIN_SERVICE_ACCOUNT_PROVIDER_ID + | typeof FATHOM_SERVICE_ACCOUNT_PROVIDER_ID + | typeof CHECKR_SERVICE_ACCOUNT_PROVIDER_ID + | typeof CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID + | typeof ASHBY_SERVICE_ACCOUNT_PROVIDER_ID | typeof FLINT_SERVICE_ACCOUNT_PROVIDER_ID | typeof REDUCTO_SERVICE_ACCOUNT_PROVIDER_ID | typeof QUIVER_SERVICE_ACCOUNT_PROVIDER_ID @@ -1476,6 +1510,285 @@ export const TOKEN_SERVICE_ACCOUNT_DESCRIPTORS: Record< docsUrl: 'https://docs.sim.ai/integrations/flint', helpText: 'Create a key in your Flint team settings. It needs at least the member role.', }, + [ASHBY_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: ASHBY_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Ashby', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { id: 'apiToken', label: 'API key', placeholder: 'Paste your Ashby API key', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/ashby', + helpText: + 'Create an API key in Ashby under Admin > API Keys and grant read and write access to the modules your workflows use.', + invalidCredentialsHelp: + 'Ashby rejected this key. Check that it was copied in full and has not been deactivated.', + }, + [CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Calendly', + tokenNoun: 'personal access token', + connectNoun: 'personal access token', + fields: [ + { + id: 'apiToken', + label: 'Personal access token', + placeholder: 'Paste your Calendly personal access token', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/calendly', + helpText: + 'Create a personal access token in Calendly under Settings > Integrations > API & Webhooks.', + }, + [CHECKR_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: CHECKR_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Checkr', + tokenNoun: 'secret API key', + connectNoun: 'API key', + fields: [ + { + id: 'apiToken', + label: 'Secret API key', + placeholder: 'Paste your Checkr secret API key', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/checkr', + helpText: + 'Use a secret API key from your Checkr production account (Account Settings > Developer Settings). Staging keys do not work, since Sim calls api.checkr.com.', + invalidCredentialsHelp: + 'Checkr rejected this key. Check that it is a production secret key, not a publishable or staging key.', + }, + [FATHOM_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: FATHOM_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Fathom', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { id: 'apiToken', label: 'API key', placeholder: 'Paste your Fathom API key', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/fathom', + helpText: + 'Create an API key in Fathom under User Settings > API Access. The key sees the meetings you recorded or that were shared with you or your team.', + }, + [GRAIN_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: GRAIN_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Grain', + tokenNoun: 'access token', + connectNoun: 'access token', + fields: [ + { + id: 'apiToken', + label: 'Access token', + placeholder: 'Paste your Grain access token', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/grain', + helpText: + 'Create a personal or workspace access token in Grain under Settings > Integrations > API. A personal token has the same access you have in Grain.', + }, + [GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Granola', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { id: 'apiToken', label: 'API key', placeholder: 'Paste your Granola API key', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/granola', + helpText: + 'Create an API key in the Granola desktop app under Settings > Connectors > API keys (Business or Enterprise plan). Workspace admins can create a workspace API key, which does not expire and reads public notes and spaces with API access enabled.', + }, + [LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Lemlist', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { id: 'apiToken', label: 'API key', placeholder: 'Paste your Lemlist API key', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/lemlist', + helpText: 'Find your API key in Lemlist under Settings > Integrations.', + invalidCredentialsHelp: + 'Lemlist rejected the key. A key whose user is blocked also fails; generate a new key under Settings > Integrations.', + }, + [PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'PagerDuty', + tokenNoun: 'REST API key', + connectNoun: 'API key', + fields: [ + { + id: 'apiToken', + label: 'REST API key', + placeholder: 'Paste your PagerDuty REST API key', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/pagerduty', + helpText: + 'Use an account REST API key (an administrator creates it under Integrations > API Access Keys) or a personal user API token (User Settings > API Access). Creating, updating, snoozing, merging, and noting incidents needs a full-access key; a read-only key covers the list and get operations.', + }, + [RESEND_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: RESEND_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Resend', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { + id: 'apiToken', + label: 'API key', + placeholder: 're_...', + secret: true, + hintPattern: /^re_/, + hintMessage: 'Resend API keys start with re_.', + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/resend', + helpText: + 'A sending-access key can only send emails. Use a full-access key to manage contacts, audiences, broadcasts, and domains.', + invalidCredentialsHelp: + 'Resend rejected this key. Check that it was copied in full and has not been deleted, deactivated, or suspended.', + }, + [ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Rootly', + tokenNoun: 'API key', + connectNoun: 'API key', + fields: [ + { id: 'apiToken', label: 'API key', placeholder: 'Paste your Rootly API key', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/rootly', + helpText: + 'Use a Global or Team API key. A Personal key stops working if the person who created it leaves the organization.', + invalidCredentialsHelp: + 'Rootly rejected this key. Check that it was copied in full and has not been revoked.', + }, + [SENTRY_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: SENTRY_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Sentry', + tokenNoun: 'auth token', + connectNoun: 'auth token', + fields: [ + { + id: 'apiToken', + label: 'Auth token', + placeholder: 'Paste your Sentry auth token', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/sentry', + helpText: + 'Use a user auth token or an internal integration token with the scopes your operations need, such as org:read, project:read, project:write, and event:read. The organization slug stays a field on the block.', + invalidCredentialsHelp: + 'Sentry rejected this token. Check that it was copied in full, has not been revoked, and belongs to sentry.io rather than a self-hosted Sentry.', + }, + [STRIPE_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: STRIPE_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Stripe', + tokenNoun: 'secret key', + connectNoun: 'secret key', + fields: [ + { + id: 'apiToken', + label: 'Secret key', + placeholder: 'sk_live_...', + secret: true, + hintPattern: /^(sk|rk)_(live|test)_/, + hintMessage: + 'Stripe secret keys start with sk_live_ or sk_test_ (restricted keys with rk_).', + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/stripe', + helpText: + 'Create a secret or restricted key under Developers > API keys in the Stripe Dashboard. A restricted key needs read or write access to the resources your workflows use.', + }, + [TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Telegram', + tokenNoun: 'bot token', + connectNoun: 'bot token', + fields: [ + { + id: 'apiToken', + label: 'Bot token', + placeholder: '123456789:ABC...', + secret: true, + hintPattern: /^\d+:/, + hintMessage: 'Telegram bot tokens look like 123456789:ABC-DEF...', + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/telegram', + helpText: + 'Message /newbot to @BotFather in Telegram to create a bot and copy the token it gives you.', + }, + [TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Typeform', + tokenNoun: 'personal access token', + connectNoun: 'personal access token', + fields: [ + { + id: 'apiToken', + label: 'Personal access token', + placeholder: 'Paste personal access token', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/typeform', + helpText: + 'Create a personal access token in Typeform under Account > Personal tokens. Grant the forms and responses scopes your workflows use.', + invalidCredentialsHelp: + 'Typeform rejected this token. Check that it has not been revoked and that it includes the forms:read scope.', + }, + [VERCEL_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: VERCEL_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'Vercel', + tokenNoun: 'access token', + connectNoun: 'access token', + fields: [ + { id: 'apiToken', label: 'Access token', placeholder: 'Paste access token', secret: true }, + ], + docsUrl: 'https://docs.sim.ai/integrations/vercel', + helpText: + 'Create a token under Account Settings > Tokens in the Vercel dashboard, scoped to the team your workflows manage.', + }, + [WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'WhatsApp', + tokenNoun: 'access token', + connectNoun: 'access token', + fields: [ + { + id: 'apiToken', + label: 'Access token', + placeholder: 'Paste WhatsApp Cloud API access token', + secret: true, + }, + ], + docsUrl: 'https://docs.sim.ai/integrations/whatsapp', + helpText: + 'Use a permanent system user access token from Meta Business Settings with the whatsapp_business_messaging permission. Temporary tokens from the App Dashboard expire after 24 hours.', + invalidCredentialsHelp: + 'Meta rejected this token. Check that it has not expired or been revoked; temporary App Dashboard tokens last only 24 hours.', + }, + [GITLAB_SERVICE_ACCOUNT_PROVIDER_ID]: { + providerId: GITLAB_SERVICE_ACCOUNT_PROVIDER_ID, + serviceLabel: 'GitLab', + tokenNoun: 'access token', + connectNoun: 'access token', + fields: [ + { id: 'apiToken', label: 'Access token', placeholder: 'glpat-...', secret: true }, + { id: 'domain', label: 'GitLab host', placeholder: 'gitlab.com', secret: false }, + ], + docsUrl: 'https://docs.sim.ai/integrations/gitlab', + helpText: + 'Use a personal, project, or group access token with the api scope (read_api for read-only workflows). Enter gitlab.com, or your self-managed host.', + invalidCredentialsHelp: + 'GitLab rejected this token. Check that it is active, has the api or read_api scope, and belongs to the host you entered.', + }, } /** diff --git a/apps/sim/lib/credentials/token-service-accounts/errors.ts b/apps/sim/lib/credentials/token-service-accounts/errors.ts index f817d87a83a..6118c12118f 100644 --- a/apps/sim/lib/credentials/token-service-accounts/errors.ts +++ b/apps/sim/lib/credentials/token-service-accounts/errors.ts @@ -74,6 +74,19 @@ export function isTransientProviderStatus(status: number): boolean { return status === 408 || status === 429 } +/** + * The shared split for a failed provider call: a non-transient 4xx is the + * caller's input being wrong; anything else (5xx, 408/429, or a reply that + * wasn't an error status but still failed) is the provider. + */ +export function providerFailureCode( + status: number +): Extract { + return status >= 400 && status < 500 && !isTransientProviderStatus(status) + ? 'invalid_credentials' + : 'provider_unavailable' +} + export interface FetchProviderOptions { /** * Validation code thrown when the host does not resolve (`ENOTFOUND` only — @@ -182,11 +195,9 @@ export async function throwForProviderResponse( ): Promise { if (res.ok) return const body = await readProviderErrorSnippet(res) - const callerError = - res.status >= 400 && res.status < 500 && !isTransientProviderStatus(res.status) - throw new TokenServiceAccountValidationError( - callerError ? 'invalid_credentials' : 'provider_unavailable', - res.status, - { step, body, ...context } - ) + throw new TokenServiceAccountValidationError(providerFailureCode(res.status), res.status, { + step, + body, + ...context, + }) } diff --git a/apps/sim/lib/credentials/token-service-accounts/server.ts b/apps/sim/lib/credentials/token-service-accounts/server.ts index d9d7eaad6da..19998b723cb 100644 --- a/apps/sim/lib/credentials/token-service-accounts/server.ts +++ b/apps/sim/lib/credentials/token-service-accounts/server.ts @@ -8,12 +8,15 @@ import { APIFY_SERVICE_ACCOUNT_PROVIDER_ID, APOLLO_SERVICE_ACCOUNT_PROVIDER_ID, ASANA_SERVICE_ACCOUNT_PROVIDER_ID, + ASHBY_SERVICE_ACCOUNT_PROVIDER_ID, ATTIO_SERVICE_ACCOUNT_PROVIDER_ID, BREX_SERVICE_ACCOUNT_PROVIDER_ID, BRIGHTDATA_SERVICE_ACCOUNT_PROVIDER_ID, BROWSER_USE_SERVICE_ACCOUNT_PROVIDER_ID, BUFFER_SERVICE_ACCOUNT_PROVIDER_ID, CALCOM_SERVICE_ACCOUNT_PROVIDER_ID, + CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID, + CHECKR_SERVICE_ACCOUNT_PROVIDER_ID, CIRCLEBACK_SERVICE_ACCOUNT_PROVIDER_ID, CLAUDE_PLATFORM_SERVICE_ACCOUNT_PROVIDER_ID, CLERK_SERVICE_ACCOUNT_PROVIDER_ID, @@ -28,11 +31,15 @@ import { ELEVENLABS_SERVICE_ACCOUNT_PROVIDER_ID, ENRICH_SERVICE_ACCOUNT_PROVIDER_ID, EXTEND_SERVICE_ACCOUNT_PROVIDER_ID, + FATHOM_SERVICE_ACCOUNT_PROVIDER_ID, FIREFLIES_SERVICE_ACCOUNT_PROVIDER_ID, FLINT_SERVICE_ACCOUNT_PROVIDER_ID, FLOW_SERVICE_ACCOUNT_PROVIDER_ID, GAMMA_SERVICE_ACCOUNT_PROVIDER_ID, GAUGE_SERVICE_ACCOUNT_PROVIDER_ID, + GITLAB_SERVICE_ACCOUNT_PROVIDER_ID, + GRAIN_SERVICE_ACCOUNT_PROVIDER_ID, + GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID, HARMONIC_SERVICE_ACCOUNT_PROVIDER_ID, HEX_SERVICE_ACCOUNT_PROVIDER_ID, HIGGSFIELD_SERVICE_ACCOUNT_PROVIDER_ID, @@ -44,6 +51,7 @@ import { isTokenServiceAccountProviderId, LANGSMITH_SERVICE_ACCOUNT_PROVIDER_ID, LAUNCHDARKLY_SERVICE_ACCOUNT_PROVIDER_ID, + LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID, LINEAR_SERVICE_ACCOUNT_PROVIDER_ID, LINQ_SERVICE_ACCOUNT_PROVIDER_ID, LOOPS_SERVICE_ACCOUNT_PROVIDER_ID, @@ -53,6 +61,7 @@ import { MONDAY_SERVICE_ACCOUNT_PROVIDER_ID, NOTION_SERVICE_ACCOUNT_PROVIDER_ID, OTTER_SERVICE_ACCOUNT_PROVIDER_ID, + PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID, PERSONA_SERVICE_ACCOUNT_PROVIDER_ID, PIPEDRIVE_SERVICE_ACCOUNT_PROVIDER_ID, PROFOUND_SERVICE_ACCOUNT_PROVIDER_ID, @@ -61,9 +70,12 @@ import { QUIVER_SERVICE_ACCOUNT_PROVIDER_ID, RB2B_SERVICE_ACCOUNT_PROVIDER_ID, REDUCTO_SERVICE_ACCOUNT_PROVIDER_ID, + RESEND_SERVICE_ACCOUNT_PROVIDER_ID, RIPPLING_SERVICE_ACCOUNT_PROVIDER_ID, ROCKETLANE_SERVICE_ACCOUNT_PROVIDER_ID, + ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID, SENDGRID_SERVICE_ACCOUNT_PROVIDER_ID, + SENTRY_SERVICE_ACCOUNT_PROVIDER_ID, SHOPIFY_SERVICE_ACCOUNT_PROVIDER_ID, SIMILARWEB_SERVICE_ACCOUNT_PROVIDER_ID, SIXTYFOUR_SERVICE_ACCOUNT_PROVIDER_ID, @@ -71,15 +83,20 @@ import { SNOWFLAKE_SERVICE_ACCOUNT_PROVIDER_ID, SPORTMONKS_SERVICE_ACCOUNT_PROVIDER_ID, SQUARE_SERVICE_ACCOUNT_PROVIDER_ID, + STRIPE_SERVICE_ACCOUNT_PROVIDER_ID, TAILSCALE_SERVICE_ACCOUNT_PROVIDER_ID, TAVILY_SERVICE_ACCOUNT_PROVIDER_ID, + TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID, TOKEN_SERVICE_ACCOUNT_SECRET_TYPE, type TokenServiceAccountProviderId, TRELLO_SERVICE_ACCOUNT_PROVIDER_ID, TRIGGER_DEV_SERVICE_ACCOUNT_PROVIDER_ID, + TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID, UPTIMEROBOT_SERVICE_ACCOUNT_PROVIDER_ID, + VERCEL_SERVICE_ACCOUNT_PROVIDER_ID, WEALTHBOX_SERVICE_ACCOUNT_PROVIDER_ID, WEBFLOW_SERVICE_ACCOUNT_PROVIDER_ID, + WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID, YOUCOM_SERVICE_ACCOUNT_PROVIDER_ID, YOUTUBE_SERVICE_ACCOUNT_PROVIDER_ID, ZEP_SERVICE_ACCOUNT_PROVIDER_ID, @@ -93,6 +110,7 @@ import { validateClickupServiceAccount } from '@/lib/credentials/token-service-a import { validateCodaServiceAccount } from '@/lib/credentials/token-service-accounts/validators/coda' import { validateFlowServiceAccount } from '@/lib/credentials/token-service-accounts/validators/flow' import { validateGaugeServiceAccount } from '@/lib/credentials/token-service-accounts/validators/gauge' +import { validateGitLabServiceAccount } from '@/lib/credentials/token-service-accounts/validators/gitlab' import { validateHarmonicServiceAccount } from '@/lib/credentials/token-service-accounts/validators/harmonic' import { validateHiggsfieldServiceAccount } from '@/lib/credentials/token-service-accounts/validators/higgsfield' import { validateHubspotServiceAccount } from '@/lib/credentials/token-service-accounts/validators/hubspot' @@ -109,10 +127,13 @@ import { validateAhrefsServiceAccount, validateApifyServiceAccount, validateApolloServiceAccount, + validateAshbyServiceAccount, validateBrexServiceAccount, validateBrightDataServiceAccount, validateBrowserUseServiceAccount, validateBufferServiceAccount, + validateCalendlyServiceAccount, + validateCheckrServiceAccount, validateCirclebackServiceAccount, validateClerkServiceAccount, validateCloudflareServiceAccount, @@ -124,9 +145,12 @@ import { validateElevenLabsServiceAccount, validateEnrichServiceAccount, validateExtendServiceAccount, + validateFathomServiceAccount, validateFirefliesServiceAccount, validateFlintServiceAccount, validateGammaServiceAccount, + validateGrainServiceAccount, + validateGranolaServiceAccount, validateHexServiceAccount, validateHuggingfaceServiceAccount, validateIncidentioServiceAccount, @@ -134,12 +158,14 @@ import { validateIntercomServiceAccount, validateLangsmithServiceAccount, validateLaunchdarklyServiceAccount, + validateLemlistServiceAccount, validateLinqServiceAccount, validateLoopsServiceAccount, validateLumaServiceAccount, validateMailchimpServiceAccount, validateMem0ServiceAccount, validateOtterServiceAccount, + validatePagerDutyServiceAccount, validatePersonaServiceAccount, validateProfoundServiceAccount, validatePulseServiceAccount, @@ -147,18 +173,26 @@ import { validateQuiverServiceAccount, validateRB2BServiceAccount, validateReductoServiceAccount, + validateResendServiceAccount, validateRipplingServiceAccount, validateRocketlaneServiceAccount, + validateRootlyServiceAccount, validateSendGridServiceAccount, + validateSentryServiceAccount, validateSimilarwebServiceAccount, validateSixtyfourServiceAccount, validateSmartleadServiceAccount, validateSportmonksServiceAccount, validateSquareServiceAccount, + validateStripeServiceAccount, validateTailscaleServiceAccount, validateTavilyServiceAccount, + validateTelegramServiceAccount, validateTriggerDevServiceAccount, + validateTypeformServiceAccount, validateUptimeRobotServiceAccount, + validateVercelServiceAccount, + validateWhatsAppServiceAccount, validateYouComServiceAccount, validateYouTubeServiceAccount, validateZepServiceAccount, @@ -292,6 +326,23 @@ const TOKEN_SERVICE_ACCOUNT_VALIDATORS: Record< [AGENTMAIL_SERVICE_ACCOUNT_PROVIDER_ID]: validateAgentMailServiceAccount, [AFFINITY_SERVICE_ACCOUNT_PROVIDER_ID]: validateAffinityServiceAccount, [TAVILY_SERVICE_ACCOUNT_PROVIDER_ID]: validateTavilyServiceAccount, + [GITLAB_SERVICE_ACCOUNT_PROVIDER_ID]: validateGitLabServiceAccount, + [ASHBY_SERVICE_ACCOUNT_PROVIDER_ID]: validateAshbyServiceAccount, + [CALENDLY_SERVICE_ACCOUNT_PROVIDER_ID]: validateCalendlyServiceAccount, + [CHECKR_SERVICE_ACCOUNT_PROVIDER_ID]: validateCheckrServiceAccount, + [FATHOM_SERVICE_ACCOUNT_PROVIDER_ID]: validateFathomServiceAccount, + [GRAIN_SERVICE_ACCOUNT_PROVIDER_ID]: validateGrainServiceAccount, + [GRANOLA_SERVICE_ACCOUNT_PROVIDER_ID]: validateGranolaServiceAccount, + [LEMLIST_SERVICE_ACCOUNT_PROVIDER_ID]: validateLemlistServiceAccount, + [PAGERDUTY_SERVICE_ACCOUNT_PROVIDER_ID]: validatePagerDutyServiceAccount, + [RESEND_SERVICE_ACCOUNT_PROVIDER_ID]: validateResendServiceAccount, + [ROOTLY_SERVICE_ACCOUNT_PROVIDER_ID]: validateRootlyServiceAccount, + [SENTRY_SERVICE_ACCOUNT_PROVIDER_ID]: validateSentryServiceAccount, + [STRIPE_SERVICE_ACCOUNT_PROVIDER_ID]: validateStripeServiceAccount, + [TELEGRAM_SERVICE_ACCOUNT_PROVIDER_ID]: validateTelegramServiceAccount, + [TYPEFORM_SERVICE_ACCOUNT_PROVIDER_ID]: validateTypeformServiceAccount, + [VERCEL_SERVICE_ACCOUNT_PROVIDER_ID]: validateVercelServiceAccount, + [WHATSAPP_SERVICE_ACCOUNT_PROVIDER_ID]: validateWhatsAppServiceAccount, } export function getTokenServiceAccountValidator( diff --git a/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.test.ts b/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.test.ts new file mode 100644 index 00000000000..044ab9cbf0a --- /dev/null +++ b/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.test.ts @@ -0,0 +1,84 @@ +/** + * @vitest-environment node + */ +import { jsonResponse } from '@sim/testing/helpers/http' +import { + inputValidationMock, + inputValidationMockFns, +} from '@sim/testing/mocks/input-validation.mock' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/core/security/input-validation.server', () => inputValidationMock) + +import { validateGitLabServiceAccount } from '@/lib/credentials/token-service-accounts/validators/gitlab' + +const activeToken = { name: 'sim-workflows', active: true, revoked: false, scopes: ['api'] } + +describe('GitLab saved token verification', () => { + const fetch = inputValidationMockFns.mockSecureFetchWithValidation + + beforeEach(() => { + fetch.mockReset() + }) + + it('verifies the token at the host it was typed with and stores that host', async () => { + fetch.mockResolvedValue(jsonResponse(activeToken)) + + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'https://gitlab.example.com/' }) + ).resolves.toMatchObject({ normalizedDomain: 'gitlab.example.com' }) + expect(fetch).toHaveBeenCalledWith( + 'https://gitlab.example.com/api/v4/personal_access_tokens/self', + expect.objectContaining({ maxRedirects: 0, profile: 'configuredEndpoint' }) + ) + }) + + it('refuses a host that could send the token somewhere else, without calling it', async () => { + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'evil.example.com@gitlab.com' }) + ).rejects.toMatchObject({ code: 'invalid_credentials' }) + expect(fetch).not.toHaveBeenCalled() + }) + + it.each([ + ['a revoked token', { ...activeToken, active: false, revoked: true }], + ['a token without an API scope', { ...activeToken, scopes: ['read_repository'] }], + ])('rejects %s', async (_, token) => { + fetch.mockResolvedValue(jsonResponse(token)) + + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'gitlab.com' }) + ).rejects.toMatchObject({ code: 'invalid_credentials' }) + }) + + it.each([ + ['an HTML page', new Response('', { status: 200 })], + ['JSON that is not a token lookup', jsonResponse({ ok: true })], + ])('reports %s as GitLab misbehaving, not as a bad token', async (_, reply) => { + fetch.mockResolvedValue(reply) + + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'gitlab.com' }) + ).rejects.toMatchObject({ code: 'provider_unavailable' }) + }) + + it('reports a host that does not exist as not found, so the user fixes it instead of retrying', async () => { + fetch.mockRejectedValue( + new Error('url hostname could not be resolved', { + cause: Object.assign(new Error('getaddrinfo ENOTFOUND'), { code: 'ENOTFOUND' }), + }) + ) + + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'gitlab.exmaple.com' }) + ).rejects.toMatchObject({ code: 'site_not_found' }) + }) + + it('reports an unreachable GitLab instance as unavailable, not as a bad token', async () => { + fetch.mockResolvedValue(jsonResponse({ message: 'Bad Gateway' }, 502)) + + await expect( + validateGitLabServiceAccount({ apiToken: 'glpat-x', domain: 'gitlab.com' }) + ).rejects.toMatchObject({ code: 'provider_unavailable' }) + }) +}) diff --git a/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.ts b/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.ts new file mode 100644 index 00000000000..afbc45fe495 --- /dev/null +++ b/apps/sim/lib/credentials/token-service-accounts/validators/gitlab.ts @@ -0,0 +1,96 @@ +import { toRecord } from '@sim/utils/object' +import { + type SecureFetchResponse, + secureFetchWithValidation, +} from '@/lib/core/security/input-validation.server' +import { + providerFailureCode, + TokenServiceAccountValidationError, +} from '@/lib/credentials/token-service-accounts/errors' +import type { + TokenServiceAccountFields, + TokenServiceAccountValidationResult, +} from '@/lib/credentials/token-service-accounts/server' +import { normalizeGitLabHost } from '@/tools/gitlab/utils' + +/** Scopes that let the GitLab tools call the REST API. */ +const API_SCOPES = new Set(['api', 'read_api']) + +/** + * Verifies a GitLab personal, project, or group access token against the + * instance it was typed with (gitlab.com or self-managed). GitLab's token + * self-lookup answers for any of the three; the token must be active and carry + * an API scope. The host is normalized exactly as the tools normalize it and + * stored as the credential's domain, so runs only ever reach the verified host. + */ +export async function validateGitLabServiceAccount( + fields: TokenServiceAccountFields +): Promise { + let host: string + try { + host = normalizeGitLabHost(fields.domain) + } catch { + throw new TokenServiceAccountValidationError('invalid_credentials', 400, { + step: 'verify', + reason: 'invalid host', + }) + } + let response: SecureFetchResponse + try { + response = await secureFetchWithValidation( + `https://${host}/api/v4/personal_access_tokens/self`, + { + profile: 'configuredEndpoint', + headers: { 'PRIVATE-TOKEN': fields.apiToken, Accept: 'application/json' }, + timeout: 10_000, + maxResponseBytes: 64 * 1024, + maxRedirects: 0, + logUrlValidationDetails: false, + } + ) + } catch (error) { + // Only ENOTFOUND proves the host doesn't exist; other lookup and network failures may pass. + if ((error as { cause?: { code?: unknown } })?.cause?.code === 'ENOTFOUND') { + throw new TokenServiceAccountValidationError('site_not_found', 400, { + step: 'verify', + reason: 'host does not resolve', + }) + } + throw new TokenServiceAccountValidationError('provider_unavailable', 502, { + step: 'verify', + reason: 'could not reach the GitLab host', + }) + } + if (!response.ok) { + await response.body?.cancel().catch(() => undefined) + throw new TokenServiceAccountValidationError( + providerFailureCode(response.status), + response.status, + { step: 'verify' } + ) + } + const token = toRecord(await response.json().catch(() => null)) + if (typeof token.active !== 'boolean' || !Array.isArray(token.scopes)) { + throw new TokenServiceAccountValidationError('provider_unavailable', 502, { + step: 'verify', + reason: 'GitLab returned an unexpected token lookup', + }) + } + if ( + !token.active || + token.revoked === true || + !token.scopes.some((scope) => API_SCOPES.has(String(scope))) + ) { + throw new TokenServiceAccountValidationError('invalid_credentials', 400, { + step: 'verify', + reason: 'inactive token or no API scope', + }) + } + const name = typeof token.name === 'string' && token.name.trim() ? token.name.trim() : 'GitLab' + return { + displayName: `${name} (${host})`, + principal: null, + normalizedDomain: host, + auditMetadata: { host }, + } +} diff --git a/apps/sim/lib/credentials/token-service-accounts/validators/http.ts b/apps/sim/lib/credentials/token-service-accounts/validators/http.ts index ebe6762c5e7..301332a9619 100644 --- a/apps/sim/lib/credentials/token-service-accounts/validators/http.ts +++ b/apps/sim/lib/credentials/token-service-accounts/validators/http.ts @@ -2,7 +2,7 @@ import { truncate } from '@sim/utils/string' import { readResponseJsonWithLimit } from '@/lib/core/utils/stream-limits' import { fetchProvider, - isTransientProviderStatus, + providerFailureCode, TokenServiceAccountValidationError, throwForProviderResponse, } from '@/lib/credentials/token-service-accounts/errors' @@ -46,6 +46,13 @@ interface TokenCheck { * before {@link forbiddenMeansScoped} accepts a 403. */ rejectsKey?: (errorBody: unknown) => boolean + /** + * For a vendor that answers a valid but restricted key with an error naming + * the missing permission (Resend's `restricted_api_key` 401): returns whether + * the error body proves the key authenticated. Checked after + * {@link rejectsKey}. + */ + provesKey?: (errorBody: unknown) => boolean } const MAX_CHECK_RESPONSE_BYTES = 64 * 1024 @@ -82,11 +89,16 @@ export function tokenCheckValidator(check: TokenCheck): TokenServiceAccountValid check.forbiddenMeansScoped && response.status === 403 && (response.headers.get('content-type') ?? '').includes('application/json') - if (check.rejectsKey && !response.ok) { + if ((check.rejectsKey || check.provesKey) && !response.ok) { const unreadable = Symbol('unreadable') const errorBody = await readJsonBody(response, check.serviceLabel).catch(() => unreadable) - if (errorBody !== unreadable && check.rejectsKey(errorBody)) { - throw new TokenServiceAccountValidationError('invalid_credentials', 400, { step: 'verify' }) + if (errorBody !== unreadable) { + if (check.rejectsKey?.(errorBody)) { + throw new TokenServiceAccountValidationError('invalid_credentials', 400, { + step: 'verify', + }) + } + if (check.provesKey?.(errorBody)) return accepted } if (scopedForbidden) { // Only a body that was read and doesn't name a bad key proves the token. @@ -98,12 +110,8 @@ export function tokenCheckValidator(check: TokenCheck): TokenServiceAccountValid } return accepted } - const callerError = - response.status >= 400 && - response.status < 500 && - !isTransientProviderStatus(response.status) throw new TokenServiceAccountValidationError( - callerError ? 'invalid_credentials' : 'provider_unavailable', + providerFailureCode(response.status), response.status, { step: 'verify', diff --git a/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.test.ts b/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.test.ts index 2bcb9c30306..f0c6411986d 100644 --- a/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.test.ts +++ b/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.test.ts @@ -1,8 +1,15 @@ import { jsonResponse } from '@sim/testing/helpers/http' import { describe, expect, it, vi } from 'vitest' import { + validateAshbyServiceAccount, validateBrexServiceAccount, + validateElevenLabsServiceAccount, validateFlintServiceAccount, + validateInstantlyServiceAccount, + validateResendServiceAccount, + validateTypeformServiceAccount, + validateVercelServiceAccount, + validateYouTubeServiceAccount, } from '@/lib/credentials/token-service-accounts/validators/token-checks' describe('Flint saved API key verification', () => { @@ -92,3 +99,196 @@ describe('Brex saved token verification', () => { }) }) }) + +describe('saved key verification for vendors with scoped keys', () => { + it('accepts a valid Instantly key that lacks the campaigns scope', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue(jsonResponse({ message: 'Insufficient scope' }, 403)) + ) + + await expect(validateInstantlyServiceAccount({ apiToken: 'leads-only' })).resolves.toBeTruthy() + }) + + it('accepts a valid YouTube key that is out of quota for today', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ error: { code: 403, errors: [{ reason: 'quotaExceeded' }] } }, 403) + ) + ) + + await expect(validateYouTubeServiceAccount({ apiToken: 'AIza-valid' })).resolves.toBeTruthy() + }) + + it('rejects a YouTube key whose project has the API turned off', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ error: { code: 403, errors: [{ reason: 'accessNotConfigured' }] } }, 403) + ) + ) + + await expect(validateYouTubeServiceAccount({ apiToken: 'AIza-off' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) +}) + +describe('Ashby saved API key verification', () => { + it('accepts a valid key that lacks the read permission for this endpoint', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ success: false, errorInfo: { code: 'missing_endpoint_permission' } }, 403) + ) + ) + + await expect(validateAshbyServiceAccount({ apiToken: 'scoped' })).resolves.toMatchObject({ + displayName: 'Ashby', + }) + }) + + it('accepts a valid key whose missing permission Ashby names in its errors list', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse( + { success: false, errors: [{ message: 'missing_endpoint_permission' }] }, + 403 + ) + ) + ) + + await expect(validateAshbyServiceAccount({ apiToken: 'scoped' })).resolves.toMatchObject({ + displayName: 'Ashby', + }) + }) + + it('rejects a deactivated key Ashby answers with a 403', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ success: false, errorInfo: { code: 'invalid_api_key' } }, 403) + ) + ) + + await expect(validateAshbyServiceAccount({ apiToken: 'revoked' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) +}) + +describe('Resend saved API key verification', () => { + it('accepts a sending-only key Resend refuses for listing domains', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue(jsonResponse({ name: 'restricted_api_key', message: 'restricted' }, 401)) + ) + + await expect(validateResendServiceAccount({ apiToken: 're_send' })).resolves.toMatchObject({ + displayName: 'Resend', + }) + }) + + it('rejects an unknown key', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ name: 'validation_error', message: 'API key is invalid' }, 401) + ) + ) + + await expect(validateResendServiceAccount({ apiToken: 're_bad' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) +}) + +describe('Typeform saved token verification', () => { + it('rejects an unknown token Typeform answers with a 403', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue(jsonResponse({ code: 'AUTHENTICATION_FAILED' }, 403)) + ) + + await expect(validateTypeformServiceAccount({ apiToken: 'bad' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) +}) + +describe('Vercel saved token verification', () => { + it('rejects an unknown token Vercel flags as invalid in its 403', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue(jsonResponse({ error: { code: 'forbidden', invalidToken: true } }, 403)) + ) + + await expect(validateVercelServiceAccount({ apiToken: 'bad' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) + + it('accepts a valid token without access to the user record', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue(jsonResponse({ error: { code: 'forbidden' } }, 403)) + ) + + await expect(validateVercelServiceAccount({ apiToken: 'scoped' })).resolves.toMatchObject({ + displayName: 'Vercel', + }) + }) +}) + +describe('ElevenLabs saved API key verification', () => { + it('accepts a valid key without the user permission', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse({ detail: { status: 'missing_permissions', message: 'user_read' } }, 401) + ) + ) + + await expect( + validateElevenLabsServiceAccount({ apiToken: 'sk_scoped' }) + ).resolves.toMatchObject({ displayName: 'ElevenLabs' }) + }) + + it('rejects an unknown key', async () => { + vi.stubGlobal( + 'fetch', + vi + .fn() + .mockResolvedValue( + jsonResponse( + { detail: { status: 'invalid_api_key', message: 'API key is invalid.' } }, + 400 + ) + ) + ) + + await expect(validateElevenLabsServiceAccount({ apiToken: 'sk_bad' })).rejects.toMatchObject({ + code: 'invalid_credentials', + }) + }) +}) diff --git a/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.ts b/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.ts index cf992a1104a..3ac75516b5c 100644 --- a/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.ts +++ b/apps/sim/lib/credentials/token-service-accounts/validators/token-checks.ts @@ -12,6 +12,10 @@ import { readJsonBody, tokenCheckValidator, } from '@/lib/credentials/token-service-accounts/validators/http' +import { ashbyAuthHeaders } from '@/tools/ashby/utils' +import { checkrHeaders } from '@/tools/checkr/utils' +import { GRANOLA_API_BASE, granolaHeaders } from '@/tools/granola/utils' +import { WHATSAPP_GRAPH_VERSION } from '@/tools/whatsapp/utils' /** * Token checks for saved-credential providers whose key is verified by one @@ -87,6 +91,29 @@ export const validateApolloServiceAccount = tokenCheckValidator({ }), }) +/** + * Listing Ashby candidate sources answers 200 for a valid key. Ashby refuses an + * unknown, wrong or deactivated key with 401 or 403; a valid key without the + * module's read permission gets an error naming `missing_endpoint_permission`, + * which still proves the key. + */ +export const validateAshbyServiceAccount = tokenCheckValidator({ + serviceLabel: 'Ashby', + request: ({ apiToken }) => ({ + url: 'https://api.ashbyhq.com/source.list', + method: 'POST', + headers: ashbyAuthHeaders(apiToken), + body: '{}', + }), + provesKey: (errorBody) => { + const { errorInfo, errors } = toRecord(errorBody) + const messages = toArray(errors).map((entry) => + typeof entry === 'string' ? entry : toRecord(entry).message + ) + return [toRecord(errorInfo).code, ...messages].includes('missing_endpoint_permission') + }, +}) + /** * Brex's current-user endpoint answers 200 for a valid user token. An unknown * token gets the same JSON 403 as a token missing the user scope, told apart @@ -143,6 +170,28 @@ export const validateBufferServiceAccount = tokenCheckValidator({ }, }) +/** + * Calendly's current-user endpoint answers 200 for a valid token and 401 + * otherwise; a valid token without the `users:read` scope gets a JSON 403. + */ +export const validateCalendlyServiceAccount = tokenCheckValidator({ + serviceLabel: 'Calendly', + forbiddenMeansScoped: true, + request: ({ apiToken }) => ({ + url: 'https://api.calendly.com/users/me', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + +/** Checkr's account endpoint answers 200 for a valid production secret key and 401 otherwise. */ +export const validateCheckrServiceAccount = tokenCheckValidator({ + serviceLabel: 'Checkr', + request: ({ apiToken }) => ({ + url: 'https://api.checkr.com/v1/account', + headers: checkrHeaders(apiToken), + }), +}) + /** Listing Circleback tags answers 200 for a valid key and 401 otherwise. */ export const validateCirclebackServiceAccount = tokenCheckValidator({ serviceLabel: 'Circleback', @@ -225,13 +274,19 @@ export const validateDubServiceAccount = tokenCheckValidator({ forbiddenMeansScoped: true, }) -/** ElevenLabs returns the key owner's account for a valid key and 401 otherwise. */ +/** + * ElevenLabs returns the key owner's account for a valid key and 400/401 for a bad one. A key + * without the user permission gets a 401 naming `missing_permissions`, or a 403, both of which + * still prove it authenticated. + */ export const validateElevenLabsServiceAccount = tokenCheckValidator({ serviceLabel: 'ElevenLabs', request: ({ apiToken }) => ({ url: 'https://api.elevenlabs.io/v1/user', headers: { 'xi-api-key': apiToken, Accept: 'application/json' }, }), + forbiddenMeansScoped: true, + provesKey: (errorBody) => toRecord(toRecord(errorBody).detail).status === 'missing_permissions', }) /** Enrich's credit-balance endpoint costs no credits and succeeds only for a valid key. */ @@ -256,6 +311,15 @@ export const validateExtendServiceAccount = tokenCheckValidator({ }), }) +/** Fathom's list-teams endpoint answers 200 for a valid API key and 401 otherwise. */ +export const validateFathomServiceAccount = tokenCheckValidator({ + serviceLabel: 'Fathom', + request: ({ apiToken }) => ({ + url: 'https://api.fathom.ai/external/v1/teams', + headers: { 'X-Api-Key': apiToken, Accept: 'application/json' }, + }), +}) + /** * Fireflies' GraphQL `user` query returns the key owner for a valid key. A * rejected key comes back as HTTP 500 with an `auth_failed` error. @@ -328,6 +392,37 @@ export const validateGammaServiceAccount = tokenCheckValidator({ }), }) +/** + * Grain's list-teams endpoint (a read-only POST, as the tools call it) answers 200 for a valid + * access token and 401 `invalid_token` otherwise. + */ +export const validateGrainServiceAccount = tokenCheckValidator({ + serviceLabel: 'Grain', + request: ({ apiToken }) => ({ + url: 'https://api.grain.com/_/public-api/v2/teams', + method: 'POST', + headers: { + Authorization: `Bearer ${apiToken}`, + 'Public-Api-Version': '2025-10-31', + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }), +}) + +/** + * Listing one Granola note answers 200 for a valid personal or workspace API key and 401 otherwise. + * An Enterprise admin can turn off note scopes for members, so a JSON 403 still proves the key. + */ +export const validateGranolaServiceAccount = tokenCheckValidator({ + serviceLabel: 'Granola', + forbiddenMeansScoped: true, + request: ({ apiToken }) => ({ + url: `${GRANOLA_API_BASE}/notes?page_size=1`, + headers: { ...granolaHeaders(apiToken), Accept: 'application/json' }, + }), +}) + /** Hex's list-projects endpoint (the one the Hex tools call) answers 200 for a valid token and 401 otherwise. */ export const validateHexServiceAccount = tokenCheckValidator({ serviceLabel: 'Hex', @@ -355,13 +450,17 @@ export const validateIncidentioServiceAccount = tokenCheckValidator({ }), }) -/** Instantly's campaign list (the one the Instantly tools call) answers 200 for a valid key and 401 otherwise. */ +/** + * Instantly's campaign list (the one the Instantly tools call) answers 200 for a valid key and + * 401 for a bad one; a valid key without the campaigns scope gets 403. + */ export const validateInstantlyServiceAccount = tokenCheckValidator({ serviceLabel: 'Instantly', request: ({ apiToken }) => ({ url: 'https://api.instantly.ai/api/v2/campaigns?limit=1', headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, }), + forbiddenMeansScoped: true, }) /** Intercom's identify-admin endpoint answers 200 with the token's admin and 401 to an unknown token. */ @@ -395,6 +494,21 @@ export const validateLaunchdarklyServiceAccount = tokenCheckValidator({ }), }) +/** + * Lemlist's team endpoint answers 200 for a valid API key and 401 otherwise; the key is the + * Basic-auth password with an empty username, as the tools send it. + */ +export const validateLemlistServiceAccount = tokenCheckValidator({ + serviceLabel: 'Lemlist', + request: ({ apiToken }) => ({ + url: 'https://api.lemlist.com/api/team', + headers: { + Authorization: `Basic ${Buffer.from(`:${apiToken}`).toString('base64')}`, + Accept: 'application/json', + }, + }), +}) + /** Linq's phone-number list (the one the Linq tools call) answers 200 for a valid key and 401 otherwise. */ export const validateLinqServiceAccount = tokenCheckValidator({ serviceLabel: 'Linq', @@ -465,6 +579,22 @@ export const validateOtterServiceAccount = tokenCheckValidator({ }), }) +/** + * Listing one PagerDuty user answers 200 for a valid REST API key and 401 for an unknown one; a + * JSON 403 comes only from an authenticated key whose user lacks access to the user list. + */ +export const validatePagerDutyServiceAccount = tokenCheckValidator({ + serviceLabel: 'PagerDuty', + request: ({ apiToken }) => ({ + url: 'https://api.pagerduty.com/users?limit=1', + headers: { + Authorization: `Token token=${apiToken}`, + Accept: 'application/vnd.pagerduty+json;version=2', + }, + }), + forbiddenMeansScoped: true, +}) + /** Listing one inquiry answers 200 for a valid key with inquiry read access and 401 otherwise. */ export const validatePersonaServiceAccount = tokenCheckValidator({ serviceLabel: 'Persona', @@ -533,6 +663,20 @@ export const validateReductoServiceAccount = tokenCheckValidator({ }), }) +/** + * Resend's list-domains endpoint answers 200 for a full-access key. A sending-only key gets a 401 + * named `restricted_api_key`, which still proves Resend authenticated it; an unknown key gets a + * `validation_error` and an inactive or suspended one a 403, both of which reject it. + */ +export const validateResendServiceAccount = tokenCheckValidator({ + serviceLabel: 'Resend', + request: ({ apiToken }) => ({ + url: 'https://api.resend.com/domains', + headers: { Authorization: `Bearer ${apiToken.trim()}`, Accept: 'application/json' }, + }), + provesKey: (errorBody) => toRecord(errorBody).name === 'restricted_api_key', +}) + /** Rippling's current-user endpoint answers 200 for a valid token with the sso-me.read scope and 401 otherwise. */ export const validateRipplingServiceAccount = tokenCheckValidator({ serviceLabel: 'Rippling', @@ -552,6 +696,19 @@ export const validateRocketlaneServiceAccount = tokenCheckValidator({ }), }) +/** + * Listing one Rootly severity (a read every key role has) answers 200 for a valid key and 401 + * for a bad one; a key whose role can't read severities gets the vendor's JSON 403. + */ +export const validateRootlyServiceAccount = tokenCheckValidator({ + serviceLabel: 'Rootly', + forbiddenMeansScoped: true, + request: ({ apiToken }) => ({ + url: 'https://api.rootly.com/v1/severities?page%5Bsize%5D=1', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/vnd.api+json' }, + }), +}) + /** SendGrid's scopes endpoint lists the scopes of the key that calls it, so any valid key gets 200 and a bad one 401. */ export const validateSendGridServiceAccount = tokenCheckValidator({ serviceLabel: 'SendGrid', @@ -561,6 +718,20 @@ export const validateSendGridServiceAccount = tokenCheckValidator({ }), }) +/** + * Listing the token's Sentry organizations answers 200 for a valid token and 401 for a bad one; + * a token without `org:read` (an organization token, or a narrowly scoped one) gets Sentry's JSON + * 403. + */ +export const validateSentryServiceAccount = tokenCheckValidator({ + serviceLabel: 'Sentry', + forbiddenMeansScoped: true, + request: ({ apiToken }) => ({ + url: 'https://sentry.io/api/0/organizations/', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + /** Similarweb's capabilities endpoint costs no data credits and answers 200 only for a valid key. */ export const validateSimilarwebServiceAccount = tokenCheckValidator({ serviceLabel: 'Similarweb', @@ -597,22 +768,43 @@ export const validateSportmonksServiceAccount = tokenCheckValidator({ }), }) -/** Square's list-locations read answers 200 for a valid production access token and 401 otherwise. */ +/** + * Square's list-locations read answers 200 for a valid production access token and 401 for a bad + * one; a token without the merchant-profile scope gets 403. + */ export const validateSquareServiceAccount = tokenCheckValidator({ serviceLabel: 'Square', request: ({ apiToken }) => ({ url: 'https://connect.squareup.com/v2/locations', headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, }), + forbiddenMeansScoped: true, }) -/** Listing devices in the token's default tailnet (`-`) answers 200 for a valid token and 401 otherwise. */ +/** + * Listing one customer answers 200 for a valid secret key and 401 for an unknown + * one; a restricted key without customer read access gets Stripe's JSON 403. + */ +export const validateStripeServiceAccount = tokenCheckValidator({ + serviceLabel: 'Stripe', + forbiddenMeansScoped: true, + request: ({ apiToken }) => ({ + url: 'https://api.stripe.com/v1/customers?limit=1', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + +/** + * Listing devices in the token's default tailnet (`-`) answers 200 for a valid token and 401 for a + * bad one; an OAuth token without the devices scope gets 403. + */ export const validateTailscaleServiceAccount = tokenCheckValidator({ serviceLabel: 'Tailscale', request: ({ apiToken }) => ({ url: 'https://api.tailscale.com/api/v2/tailnet/-/devices', headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, }), + forbiddenMeansScoped: true, }) /** Tavily's usage endpoint answers 200 for a valid key and 401 otherwise. */ @@ -624,6 +816,15 @@ export const validateTavilyServiceAccount = tokenCheckValidator({ }), }) +/** Telegram's getMe exists to test a bot token: 200 for a valid one, 401 or 404 otherwise. */ +export const validateTelegramServiceAccount = tokenCheckValidator({ + serviceLabel: 'Telegram', + request: ({ apiToken }) => ({ + url: `https://api.telegram.org/bot${encodeURIComponent(apiToken)}/getMe`, + headers: { Accept: 'application/json' }, + }), +}) + /** Listing one run needs a valid environment secret key; Trigger.dev answers 401 for any other. */ export const validateTriggerDevServiceAccount = tokenCheckValidator({ serviceLabel: 'Trigger.dev', @@ -633,6 +834,19 @@ export const validateTriggerDevServiceAccount = tokenCheckValidator({ }), }) +/** + * Listing one form answers 200 for a token with the forms:read scope the + * Typeform tools need. Typeform answers an unknown token with a 403, so every + * refusal rejects it. + */ +export const validateTypeformServiceAccount = tokenCheckValidator({ + serviceLabel: 'Typeform', + request: ({ apiToken }) => ({ + url: 'https://api.typeform.com/forms?page_size=1', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + /** UptimeRobot's v3 user/me returns the key owner's profile, so it succeeds only for a valid key. */ export const validateUptimeRobotServiceAccount = tokenCheckValidator({ serviceLabel: 'UptimeRobot', @@ -642,6 +856,33 @@ export const validateUptimeRobotServiceAccount = tokenCheckValidator({ }), }) +/** + * Vercel's current-user endpoint answers 200 for a valid token. An unknown token + * gets a 403 flagged `invalidToken`; any other JSON 403 is a valid token + * without access to the user record. + */ +export const validateVercelServiceAccount = tokenCheckValidator({ + serviceLabel: 'Vercel', + forbiddenMeansScoped: true, + rejectsKey: (errorBody) => toRecord(toRecord(errorBody).error).invalidToken === true, + request: ({ apiToken }) => ({ + url: 'https://api.vercel.com/v2/user', + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + +/** + * The Graph API's /me answers 200 for any valid access token and a 4xx + * OAuthException (code 190) for an invalid, expired, or revoked one. + */ +export const validateWhatsAppServiceAccount = tokenCheckValidator({ + serviceLabel: 'WhatsApp', + request: ({ apiToken }) => ({ + url: `https://graph.facebook.com/${WHATSAPP_GRAPH_VERSION}/me?fields=id`, + headers: { Authorization: `Bearer ${apiToken}`, Accept: 'application/json' }, + }), +}) + /** You.com's account balance read costs no credits and answers 200 only for a valid key. */ export const validateYouComServiceAccount = tokenCheckValidator({ serviceLabel: 'You.com', @@ -651,13 +892,37 @@ export const validateYouComServiceAccount = tokenCheckValidator({ }), }) -/** Listing video categories costs one quota unit; Google answers 400/403 for an invalid or restricted key. */ +/** + * Google's 403 reasons for a valid key that is only out of quota for now. Mirrors + * `isGoogleQuotaReason` in the Google connectors, whose module is too heavy for this graph. + */ +const YOUTUBE_QUOTA_REASONS = new Set([ + 'quotaExceeded', + 'dailyLimitExceeded', + 'rateLimitExceeded', + 'userRateLimitExceeded', + 'RATE_LIMIT_EXCEEDED', +]) + +/** + * Listing video categories costs one quota unit. Google answers 400 for an invalid key and 403 + * for a restricted one or a project without the YouTube API; a 403 for spent quota still proves + * the key, so only that one is accepted. + */ export const validateYouTubeServiceAccount = tokenCheckValidator({ serviceLabel: 'YouTube', request: ({ apiToken }) => ({ url: `https://www.googleapis.com/youtube/v3/videoCategories?part=snippet®ionCode=US&key=${encodeURIComponent(apiToken)}`, headers: { Accept: 'application/json' }, }), + forbiddenMeansScoped: true, + rejectsKey: (errorBody) => { + const error = toRecord(toRecord(errorBody).error) + if (error.code !== 403) return false + return !toArray(error.errors).some((entry) => + YOUTUBE_QUOTA_REASONS.has(String(toRecord(entry).reason)) + ) + }, }) /** Listing one thread needs a valid project key; Zep answers 401 for any other. */ diff --git a/apps/sim/lib/credentials/workload-identity/issuer.test.ts b/apps/sim/lib/credentials/workload-identity/issuer.test.ts index e2306bc04f7..be20ba66e51 100644 --- a/apps/sim/lib/credentials/workload-identity/issuer.test.ts +++ b/apps/sim/lib/credentials/workload-identity/issuer.test.ts @@ -26,6 +26,7 @@ async function loadIssuer() { describe('workload identity issuer', () => { beforeEach(() => { setEnv('WORKLOAD_IDENTITY_SIGNING_KEY', rsaPem()) + setEnv('WORKLOAD_IDENTITY_ISSUER', 'https://sim.example.com/oidc/workload') }) afterEach(() => { @@ -101,6 +102,36 @@ describe('workload identity issuer', () => { expect((await workloadIdentityJwksResponse()).status).toBe(404) }) + it.each([ + 'http://sim.example.com/oidc/workload', + 'https://sim.example.com:3000/oidc/workload', + 'https://sim.example.com/oidc/workload?tenant=1', + 'https://sim.example.com/oidc/workload#keys', + ])('stays off for an issuer no cloud can fetch (%s)', async (url) => { + setEnv('WORKLOAD_IDENTITY_ISSUER', url) + const issuer = await loadIssuer() + const { workloadIdentityDiscoveryResponse } = await import( + '@/lib/credentials/workload-identity/responses' + ) + + expect(await issuer.isWorkloadIdentityConfigured()).toBe(false) + expect((await workloadIdentityDiscoveryResponse()).status).toBe(404) + }) + + it('keeps signing with the current key when the retiring key is malformed', async () => { + setEnv('WORKLOAD_IDENTITY_PREVIOUS_KEY', 'not a PEM key') + const issuer = await loadIssuer() + const token = await issuer.mintWorkloadIdentityToken({ + subject: 'sim:workspace:ws-1:credential:abc123def456', + audience: 'sts.amazonaws.com', + owner: { workspaceId: 'ws-1' }, + }) + const jwks = await issuer.getWorkloadIdentityJwks() + + expect(await issuer.isWorkloadIdentityConfigured()).toBe(true) + await expect(jwtVerify(token, createLocalJWKSet(jwks))).resolves.toBeTruthy() + }) + it('publishes nothing and refuses to sign when no signing key is configured', async () => { setEnv('WORKLOAD_IDENTITY_SIGNING_KEY', undefined) const issuer = await loadIssuer() diff --git a/apps/sim/lib/credentials/workload-identity/issuer.ts b/apps/sim/lib/credentials/workload-identity/issuer.ts index cfc743cc941..72e13424412 100644 --- a/apps/sim/lib/credentials/workload-identity/issuer.ts +++ b/apps/sim/lib/credentials/workload-identity/issuer.ts @@ -55,6 +55,42 @@ async function toPublicJwk(key: KeyObject): Promise { return { ...exported, kid, alg: WORKLOAD_IDENTITY_ALGORITHM, use: 'sig' } } +/** + * The retiring key's public JWK, or null when it is unset or unusable. A bad + * retiring key only drops that key from the JWKS; the current key keeps signing. + */ +async function loadPreviousPublicJwk(): Promise { + const previousKey = env.WORKLOAD_IDENTITY_PREVIOUS_KEY + if (!previousKey) return null + try { + return await toPublicJwk(createPublicKey(normalizePem(previousKey))) + } catch (error) { + logger.error('Workload identity previous key is set but unusable', { error }) + return null + } +} + +/** + * AWS and Databricks fetch discovery and keys from the issuer over https on the + * default port, and discovery appends paths to it, so any other issuer (or one + * with a query or fragment) can never be trusted; keyless stays off + * rather than advertising an issuer no relying party accepts. + */ +function assertPublicIssuer(issuer: string): void { + const url = new URL(issuer) + if (url.protocol !== 'https:' || url.port || url.search || url.hash) { + throw new Error( + 'Workload identity issuer must be an https URL without a port, query, or fragment' + ) + } + if (!env.WORKLOAD_IDENTITY_ISSUER?.trim()) { + logger.warn( + 'Workload identity issuer follows the app URL; set WORKLOAD_IDENTITY_ISSUER so customer trust policies survive a URL change', + { issuer } + ) + } +} + let signingMaterial: Promise | undefined /** @@ -67,18 +103,21 @@ function loadSigningMaterial(): Promise { if (!signingKey) return Promise.reject(new WorkloadIdentityNotConfiguredError()) if (!signingMaterial) { signingMaterial = (async () => { + assertPublicIssuer(getWorkloadIdentityIssuer()) const privateKey = createPrivateKey(normalizePem(signingKey)) const current = await toPublicJwk(privateKey) const keys = [current] - const previousKey = env.WORKLOAD_IDENTITY_PREVIOUS_KEY - if (previousKey) { - const previous = await toPublicJwk(createPublicKey(normalizePem(previousKey))) - if (previous.kid !== current.kid) keys.push(previous) - } + const previous = await loadPreviousPublicJwk() + if (previous && previous.kid !== current.kid) keys.push(previous) return { privateKey, kid: current.kid as string, jwks: { keys } } })() signingMaterial.catch((error: unknown) => { - logger.error('Workload identity signing key is set but unusable', { error }) + logger.error( + 'Workload identity is configured but unusable (check the signing key and issuer)', + { + error, + } + ) }) } return signingMaterial diff --git a/apps/sim/lib/integrations/credential-visibility.server.test.ts b/apps/sim/lib/integrations/credential-visibility.server.test.ts index 566ce32c374..4a339ad23d5 100644 --- a/apps/sim/lib/integrations/credential-visibility.server.test.ts +++ b/apps/sim/lib/integrations/credential-visibility.server.test.ts @@ -235,6 +235,21 @@ describe('integration credential visibility', () => { expect(disabled.isCredentialVisible(credential)).toBe(false) }) + it('keeps GitLab personal tokens visible after the GitLab block moves to a new version', () => { + getIntegrationAvailabilityMock.mockReturnValue([ + availability('gitlab_v2', 'ready', { oauthAvailable: false, serviceAccountAvailable: true }), + ]) + const visibility = createIntegrationCredentialVisibility({ + allowedIntegrationTypes: new Set(['gitlab_v2']), + blockVisibility: null, + oauthServices: SERVICES, + }) + + expect(visibility.isCredentialVisible({ providerId: 'gitlab', type: 'personal_token' })).toBe( + true + ) + }) + it('leaves non-integration credentials visible', () => { const visibility = createIntegrationCredentialVisibility({ allowedIntegrationTypes: new Set(), diff --git a/apps/sim/lib/integrations/credential-visibility.server.ts b/apps/sim/lib/integrations/credential-visibility.server.ts index a38ae4bcc05..eb0770d5653 100644 --- a/apps/sim/lib/integrations/credential-visibility.server.ts +++ b/apps/sim/lib/integrations/credential-visibility.server.ts @@ -13,6 +13,7 @@ import { getGitHubInstallationConfiguration } from '@/lib/oauth/github-installat import { GITHUB_INSTALLATION_PROVIDER_ID } from '@/lib/oauth/github-installation-types' import type { OAuthServiceMetadata } from '@/lib/oauth/types' import { getAllOAuthServices } from '@/lib/oauth/utils' +import { resolveAccessControlBlockType } from '@/lib/permission-groups/integration-allowlist' import { getBlock } from '@/blocks/registry' import { isHiddenUnder } from '@/blocks/visibility/context' @@ -157,13 +158,15 @@ export function createIntegrationCredentialVisibility({ return oauthOwnersByProviderId.get(providerId)?.some(isManagedOAuthServiceVisible) ?? false } if (type === 'personal_token') { + // Personal tokens belong to the GitLab integration, whichever block version is current. + const gitlabType = resolveAccessControlBlockType('gitlab') if ( providerId !== 'gitlab' || - (allowedIntegrationTypes && !allowedIntegrationTypes.has('gitlab')) + (allowedIntegrationTypes && !allowedIntegrationTypes.has(gitlabType)) ) return false - const block = getBlock('gitlab') - const availability = availabilityByType.get('gitlab') + const block = getBlock(gitlabType) + const availability = availabilityByType.get(gitlabType) if (!block || block.hideFromToolbar || isHiddenUnder(blockVisibility, block) || !availability) return false const state = resolveIntegrationAvailabilityStateForVisibility(availability, blockVisibility) diff --git a/apps/sim/lib/integrations/icon-mapping.ts b/apps/sim/lib/integrations/icon-mapping.ts index 3d014686f53..9b58127768d 100644 --- a/apps/sim/lib/integrations/icon-mapping.ts +++ b/apps/sim/lib/integrations/icon-mapping.ts @@ -318,6 +318,7 @@ export const blockTypeToIconMap: Record = { arxiv: ArxivIcon, asana: AsanaIcon, ashby: AshbyIcon, + ashby_v2: AshbyIcon, athena: AthenaIcon, athena_v2: AthenaIcon, attio: AttioIcon, @@ -337,8 +338,10 @@ export const blockTypeToIconMap: Record = { buffer_v2: BufferIcon, calcom: CalComIcon, calendly: CalendlyIcon, + calendly_v2: CalendlyIcon, cbinsights: CbInsightsIcon, checkr: CheckrIcon, + checkr_v2: CheckrIcon, circleback: CirclebackIcon, circleback_v2: CirclebackIcon, clay: ClayIcon, @@ -404,6 +407,7 @@ export const blockTypeToIconMap: Record = { extend: ExtendIcon, extend_v3: ExtendIcon, fathom: FathomIcon, + fathom_v2: FathomIcon, file: DocumentIcon, file_v5: DocumentIcon, findymail: FindymailIcon, @@ -421,6 +425,7 @@ export const blockTypeToIconMap: Record = { github: GithubIcon, github_v2: GithubIcon, gitlab: GitLabIcon, + gitlab_v2: GitLabIcon, gmail: GmailIcon, gmail_v2: GmailIcon, gong: GongIcon, @@ -451,8 +456,9 @@ export const blockTypeToIconMap: Record = { 'google-sheets': GoogleSheetsIcon, grafana: GrafanaIcon, grain: GrainIcon, - grain_v2: GrainIcon, + grain_v3: GrainIcon, granola: GranolaIcon, + granola_v2: GranolaIcon, greenhouse: GreenhouseIcon, greenhouse_v2: GreenhouseIcon, greptile: GreptileIcon, @@ -502,6 +508,7 @@ export const blockTypeToIconMap: Record = { launchdarkly_v2: LaunchDarklyIcon, leadmagic: LeadMagicIcon, lemlist: LemlistIcon, + lemlist_v2: LemlistIcon, linear: LinearIcon, linear_v2: LinearIcon, linkedin: LinkedInIcon, @@ -559,6 +566,7 @@ export const blockTypeToIconMap: Record = { otter_v2: OtterIcon, outlook: OutlookIcon, pagerduty: PagerDutyIcon, + pagerduty_v2: PagerDutyIcon, parallel_ai: ParallelIcon, peopledatalabs: PeopleDataLabsIcon, perplexity: PerplexityIcon, @@ -596,12 +604,14 @@ export const blockTypeToIconMap: Record = { reducto: ReductoIcon, reducto_v3: ReductoIcon, resend: ResendIcon, + resend_v2: ResendIcon, revenuecat: RevenueCatIcon, rippling: RipplingIcon, rippling_v2: RipplingIcon, rocketlane: RocketlaneIcon, rocketlane_v2: RocketlaneIcon, rootly: RootlyIcon, + rootly_v2: RootlyIcon, rss: RssIcon, s3: S3Icon, s3_v2: S3Icon, @@ -616,6 +626,7 @@ export const blockTypeToIconMap: Record = { sendgrid: SendgridIcon, sendgrid_v2: SendgridIcon, sentry: SentryIcon, + sentry_v2: SentryIcon, serper: SerperIcon, servicenow: ServiceNowIcon, servicenow_v2: ServiceNowIcon, @@ -651,6 +662,7 @@ export const blockTypeToIconMap: Record = { ssm_v2: SSMIcon, stagehand: StagehandIcon, stripe: StripeIcon, + stripe_v2: StripeIcon, sts: STSIcon, sts_v2: STSIcon, stt: STTIcon, @@ -663,6 +675,7 @@ export const blockTypeToIconMap: Record = { tavily: TavilyIcon, tavily_v2: TavilyIcon, telegram: TelegramIcon, + telegram_v2: TelegramIcon, temporal: TemporalIcon, textract: TextractIcon, textract_v3: TextractIcon, @@ -677,14 +690,17 @@ export const blockTypeToIconMap: Record = { twilio_sms: TwilioIcon, twilio_voice: TwilioIcon, typeform: TypeformIcon, + typeform_v2: TypeformIcon, upstash: UpstashIcon, uptimerobot: UptimeRobotIcon, uptimerobot_v2: UptimeRobotIcon, vanta: VantaIcon, vercel: VercelIcon, + vercel_v2: VercelIcon, wealthbox: WealthboxIcon, webflow: WebflowIcon, whatsapp: WhatsAppIcon, + whatsapp_v2: WhatsAppIcon, wikipedia: WikipediaIcon, windchill: WindchillIcon, wiza: WizaIcon, diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index c571c228590..58e11cea128 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -14,6 +14,7 @@ import { ApifyIcon, ApolloIcon, AsanaIcon, + AshbyIcon, AtlassianIcon, AttioIcon, AzureIcon, @@ -24,6 +25,8 @@ import { BrowserUseIcon, BufferIcon, CalComIcon, + CalendlyIcon, + CheckrIcon, CirclebackIcon, ClaudeIcon, ClerkIcon, @@ -42,6 +45,7 @@ import { ElevenLabsIcon, EnrichSoIcon, ExtendIcon, + FathomIcon, FirefliesIcon, FivetranIcon, FlintIcon, @@ -49,6 +53,7 @@ import { GammaIcon, GaugeIcon, GithubIcon, + GitLabIcon, GmailIcon, GoogleAdsIcon, GoogleBigQueryIcon, @@ -64,6 +69,8 @@ import { GoogleSheetsIcon, GoogleTasksIcon, GoogleVaultIcon, + GrainIcon, + GranolaIcon, GreenhouseIcon, HarmonicIcon, HelpScoutIcon, @@ -81,6 +88,7 @@ import { JiraIcon, LangsmithIcon, LaunchDarklyIcon, + LemlistIcon, LinearIcon, LinkedInIcon, LinqIcon, @@ -103,6 +111,7 @@ import { OracleIcon, OtterIcon, OutlookIcon, + PagerDutyIcon, PersonaIcon, PipedriveIcon, PowerBIIcon, @@ -115,10 +124,13 @@ import { RB2BIcon, RedditIcon, ReductoIcon, + ResendIcon, RipplingIcon, RocketlaneIcon, + RootlyIcon, SalesforceIcon, SendgridIcon, + SentryIcon, ShopifyIcon, SimilarwebIcon, SixtyfourIcon, @@ -128,16 +140,21 @@ import { SportmonksIcon, SpotifyIcon, SquareIcon, + StripeIcon, TailscaleIcon, TavilyIcon, + TelegramIcon, TikTokIcon, TrelloIcon, TriggerDevIcon, + TypeformIcon, UptimeRobotIcon, VantaIcon, + VercelIcon, VertexIcon, WealthboxIcon, WebflowIcon, + WhatsAppIcon, WordpressIcon, xIcon, YouComIcon, @@ -2774,6 +2791,300 @@ export const OAUTH_PROVIDERS: Record = { }, defaultService: 'flint', }, + ashby: { + name: 'Ashby', + icon: AshbyIcon, + services: { + ashby: { + name: 'Ashby', + description: 'Manage candidates, jobs, applications, and offers in Ashby.', + providerId: 'ashby', + serviceAccountProviderId: 'ashby-service-account', + icon: AshbyIcon, + baseProviderIcon: AshbyIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'ashby', + }, + calendly: { + name: 'Calendly', + icon: CalendlyIcon, + services: { + calendly: { + name: 'Calendly', + description: 'Manage Calendly event types, scheduled events, and invitees.', + providerId: 'calendly', + serviceAccountProviderId: 'calendly-service-account', + icon: CalendlyIcon, + baseProviderIcon: CalendlyIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'calendly', + }, + checkr: { + name: 'Checkr', + icon: CheckrIcon, + services: { + checkr: { + name: 'Checkr', + description: 'Run and manage background checks with Checkr.', + providerId: 'checkr', + serviceAccountProviderId: 'checkr-service-account', + icon: CheckrIcon, + baseProviderIcon: CheckrIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'checkr', + }, + fathom: { + name: 'Fathom', + icon: FathomIcon, + services: { + fathom: { + name: 'Fathom', + description: 'Read meeting recordings, transcripts, summaries, and teams in Fathom.', + providerId: 'fathom', + serviceAccountProviderId: 'fathom-service-account', + icon: FathomIcon, + baseProviderIcon: FathomIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'fathom', + }, + grain: { + name: 'Grain', + icon: GrainIcon, + services: { + grain: { + name: 'Grain', + description: 'Read meeting recordings, transcripts, teams, and webhooks in Grain.', + providerId: 'grain', + serviceAccountProviderId: 'grain-service-account', + icon: GrainIcon, + baseProviderIcon: GrainIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'grain', + }, + granola: { + name: 'Granola', + icon: GranolaIcon, + services: { + granola: { + name: 'Granola', + description: + 'Read meeting notes, transcripts, folders, and audit events, and manage webhook endpoints in Granola.', + providerId: 'granola', + serviceAccountProviderId: 'granola-service-account', + icon: GranolaIcon, + baseProviderIcon: GranolaIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'granola', + }, + lemlist: { + name: 'Lemlist', + icon: LemlistIcon, + services: { + lemlist: { + name: 'Lemlist', + description: 'Read campaign activities and leads, and send emails from the Lemlist inbox.', + providerId: 'lemlist', + serviceAccountProviderId: 'lemlist-service-account', + icon: LemlistIcon, + baseProviderIcon: LemlistIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'lemlist', + }, + pagerduty: { + name: 'PagerDuty', + icon: PagerDutyIcon, + services: { + pagerduty: { + name: 'PagerDuty', + description: 'Manage incidents, services, and on-call schedules in PagerDuty.', + providerId: 'pagerduty', + serviceAccountProviderId: 'pagerduty-service-account', + icon: PagerDutyIcon, + baseProviderIcon: PagerDutyIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'pagerduty', + }, + resend: { + name: 'Resend', + icon: ResendIcon, + services: { + resend: { + name: 'Resend', + description: + 'Send emails and manage contacts, audiences, broadcasts, and domains in Resend.', + providerId: 'resend', + serviceAccountProviderId: 'resend-service-account', + icon: ResendIcon, + baseProviderIcon: ResendIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'resend', + }, + rootly: { + name: 'Rootly', + icon: RootlyIcon, + services: { + rootly: { + name: 'Rootly', + description: 'Manage incidents, alerts, on-call, and retrospectives in Rootly.', + providerId: 'rootly', + serviceAccountProviderId: 'rootly-service-account', + icon: RootlyIcon, + baseProviderIcon: RootlyIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'rootly', + }, + sentry: { + name: 'Sentry', + icon: SentryIcon, + services: { + sentry: { + name: 'Sentry', + description: 'Manage Sentry issues, events, projects, releases, and teams.', + providerId: 'sentry', + serviceAccountProviderId: 'sentry-service-account', + icon: SentryIcon, + baseProviderIcon: SentryIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'sentry', + }, + stripe: { + name: 'Stripe', + icon: StripeIcon, + services: { + stripe: { + name: 'Stripe', + description: 'Manage payments, customers, subscriptions, invoices, and products in Stripe.', + providerId: 'stripe', + serviceAccountProviderId: 'stripe-service-account', + icon: StripeIcon, + baseProviderIcon: StripeIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'stripe', + }, + telegram: { + name: 'Telegram', + icon: TelegramIcon, + services: { + telegram: { + name: 'Telegram', + description: 'Send and manage messages, media, and polls as your Telegram bot.', + providerId: 'telegram', + serviceAccountProviderId: 'telegram-service-account', + icon: TelegramIcon, + baseProviderIcon: TelegramIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'telegram', + }, + typeform: { + name: 'Typeform', + icon: TypeformIcon, + services: { + typeform: { + name: 'Typeform', + description: 'Read responses and insights, download files, and manage forms in Typeform.', + providerId: 'typeform', + serviceAccountProviderId: 'typeform-service-account', + icon: TypeformIcon, + baseProviderIcon: TypeformIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'typeform', + }, + vercel: { + name: 'Vercel', + icon: VercelIcon, + services: { + vercel: { + name: 'Vercel', + description: + 'Manage Vercel deployments, projects, domains, DNS, environment variables, and teams.', + providerId: 'vercel', + serviceAccountProviderId: 'vercel-service-account', + icon: VercelIcon, + baseProviderIcon: VercelIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'vercel', + }, + whatsapp: { + name: 'WhatsApp', + icon: WhatsAppIcon, + services: { + whatsapp: { + name: 'WhatsApp', + description: + 'Send messages, templates, media, and reactions through the WhatsApp Cloud API.', + providerId: 'whatsapp', + serviceAccountProviderId: 'whatsapp-service-account', + icon: WhatsAppIcon, + baseProviderIcon: WhatsAppIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'whatsapp', + }, + gitlab: { + name: 'GitLab', + icon: GitLabIcon, + services: { + gitlab: { + name: 'GitLab', + description: + 'Manage GitLab projects, issues, merge requests, pipelines, repositories, and members.', + providerId: 'gitlab', + serviceAccountProviderId: 'gitlab-service-account', + icon: GitLabIcon, + baseProviderIcon: GitLabIcon, + scopes: [], + authType: 'service_account', + }, + }, + defaultService: 'gitlab', + }, higgsfield: { name: 'Higgsfield', icon: HiggsfieldIcon, diff --git a/apps/sim/lib/oauth/types.ts b/apps/sim/lib/oauth/types.ts index cf22d2447cf..92a685fa4d5 100644 --- a/apps/sim/lib/oauth/types.ts +++ b/apps/sim/lib/oauth/types.ts @@ -115,6 +115,23 @@ export type OAuthProvider = | 'coda' | 'gauge' | 'higgsfield' + | 'gitlab' + | 'whatsapp' + | 'vercel' + | 'typeform' + | 'telegram' + | 'stripe' + | 'sentry' + | 'rootly' + | 'resend' + | 'pagerduty' + | 'lemlist' + | 'granola' + | 'grain' + | 'fathom' + | 'checkr' + | 'calendly' + | 'ashby' | 'flint' | 'reducto' | 'quiver' @@ -254,6 +271,23 @@ export type OAuthService = | 'coda' | 'gauge' | 'higgsfield' + | 'gitlab' + | 'whatsapp' + | 'vercel' + | 'typeform' + | 'telegram' + | 'stripe' + | 'sentry' + | 'rootly' + | 'resend' + | 'pagerduty' + | 'lemlist' + | 'granola' + | 'grain' + | 'fathom' + | 'checkr' + | 'calendly' + | 'ashby' | 'flint' | 'reducto' | 'quiver' diff --git a/apps/sim/lib/permission-groups/block-successors.generated.ts b/apps/sim/lib/permission-groups/block-successors.generated.ts index e792b14c858..76fce6e3bbe 100644 --- a/apps/sim/lib/permission-groups/block-successors.generated.ts +++ b/apps/sim/lib/permission-groups/block-successors.generated.ts @@ -16,13 +16,16 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { apify: 'apify_v2', apollo: 'apollo_v2', appconfig: 'appconfig_v2', + ashby: 'ashby_v2', athena: 'athena_v2', box: 'box_v2', brex: 'brex_v2', brightdata: 'brightdata_v2', browser_use: 'browser_use_v2', buffer: 'buffer_v2', + calendly: 'calendly_v2', chat_trigger: 'start_trigger', + checkr: 'checkr_v2', circleback: 'circleback_v2', clerk: 'clerk_v2', cloudflare: 'cloudflare_v2', @@ -46,6 +49,7 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { enrich: 'enrich_v2', extend: 'extend_v3', extend_v2: 'extend_v3', + fathom: 'fathom_v2', file: 'file_v5', file_v2: 'file_v5', file_v3: 'file_v5', @@ -55,11 +59,14 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { flint: 'flint_v2', gamma: 'gamma_v2', github: 'github_v2', + gitlab: 'gitlab_v2', gmail: 'gmail_v2', google_calendar: 'google_calendar_v2', google_sheets: 'google_sheets_v2', google_slides: 'google_slides_v2', - grain: 'grain_v2', + grain: 'grain_v3', + grain_v2: 'grain_v3', + granola: 'granola_v2', greenhouse: 'greenhouse_v2', hex: 'hex_v2', huggingface: 'huggingface_v2', @@ -77,6 +84,7 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { lambda: 'lambda_v2', langsmith: 'langsmith_v2', launchdarkly: 'launchdarkly_v2', + lemlist: 'lemlist_v2', linear: 'linear_v2', linq: 'linq_v2', logs: 'logs_v2', @@ -92,6 +100,7 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { notion: 'notion_v2', openai: 'embeddings', otter: 'otter_v2', + pagerduty: 'pagerduty_v2', persona: 'persona_v2', profound: 'profound_v2', pulse: 'pulse_v3', @@ -103,12 +112,15 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { rds: 'rds_v2', reducto: 'reducto_v3', reducto_v2: 'reducto_v3', + resend: 'resend_v2', rippling: 'rippling_v2', rocketlane: 'rocketlane_v2', + rootly: 'rootly_v2', router: 'router_v2', s3: 's3_v2', secrets_manager: 'secrets_manager_v2', sendgrid: 'sendgrid_v2', + sentry: 'sentry_v2', servicenow: 'servicenow_v2', ses: 'ses_v2', sftp: 'sftp_v2', @@ -123,17 +135,22 @@ export const BLOCK_ACCESS_SUCCESSORS: Record = { ssh: 'ssh_v2', ssm: 'ssm_v2', starter: 'start_trigger', + stripe: 'stripe_v2', sts: 'sts_v2', stt: 'stt_v2', table: 'table_v2', tailscale: 'tailscale_v2', tavily: 'tavily_v2', + telegram: 'telegram_v2', textract: 'textract_v3', textract_v2: 'textract_v3', trigger_dev: 'trigger_dev_v2', + typeform: 'typeform_v2', uptimerobot: 'uptimerobot_v2', + vercel: 'vercel_v2', video_generator: 'video_generator_v3', video_generator_v2: 'video_generator_v3', + whatsapp: 'whatsapp_v2', workflow: 'workflow_input', youcom: 'youcom_v2', youtube: 'youtube_v2', diff --git a/apps/sim/serializer/saved-credentials.test.ts b/apps/sim/serializer/saved-credentials.test.ts index a99368a1205..eee841241c3 100644 --- a/apps/sim/serializer/saved-credentials.test.ts +++ b/apps/sim/serializer/saved-credentials.test.ts @@ -2,6 +2,7 @@ import { createBlock } from '@sim/testing/factories/block.factory' import { blocksMock } from '@sim/testing/mocks/blocks.mock' import { getErrorMessage } from '@sim/utils/errors' import { beforeEach, describe, expect, it, vi } from 'vitest' +import { isTriggerModeSubBlock } from '@/lib/workflows/subblocks/visibility' import * as blocks from '@/blocks' import { AffinityBlock, AffinityV2Block } from '@/blocks/blocks/affinity' import { AgentMailBlock, AgentMailV2Block } from '@/blocks/blocks/agentmail' @@ -10,11 +11,14 @@ import { AhrefsBlock, AhrefsV2Block } from '@/blocks/blocks/ahrefs' import { ApifyBlock, ApifyV2Block } from '@/blocks/blocks/apify' import { ApolloBlock, ApolloV2Block } from '@/blocks/blocks/apollo' import { AppConfigBlock, AppConfigV2Block } from '@/blocks/blocks/appconfig' +import { AshbyBlock, AshbyV2Block } from '@/blocks/blocks/ashby' import { AthenaBlock, AthenaV2Block } from '@/blocks/blocks/athena' import { BrexBlock, BrexV2Block } from '@/blocks/blocks/brex' import { BrightDataBlock, BrightDataV2Block } from '@/blocks/blocks/brightdata' import { BrowserUseBlock, BrowserUseV2Block } from '@/blocks/blocks/browser_use' import { BufferBlock, BufferV2Block } from '@/blocks/blocks/buffer' +import { CalendlyBlock, CalendlyV2Block } from '@/blocks/blocks/calendly' +import { CheckrBlock, CheckrV2Block } from '@/blocks/blocks/checkr' import { CirclebackBlock, CirclebackV2Block } from '@/blocks/blocks/circleback' import { ClerkBlock, ClerkV2Block } from '@/blocks/blocks/clerk' import { CloudflareBlock, CloudflareV2Block } from '@/blocks/blocks/cloudflare' @@ -33,9 +37,13 @@ import { EC2Block, EC2V2Block } from '@/blocks/blocks/ec2' import { ElevenLabsBlock, ElevenLabsV2Block } from '@/blocks/blocks/elevenlabs' import { EnrichBlock, EnrichV2Block } from '@/blocks/blocks/enrich' import { ExtendV2Block, ExtendV3Block } from '@/blocks/blocks/extend' +import { FathomBlock, FathomV2Block } from '@/blocks/blocks/fathom' import { FirefliesV2Block, FirefliesV3Block } from '@/blocks/blocks/fireflies' import { FlintBlock, FlintV2Block } from '@/blocks/blocks/flint' import { GammaBlock, GammaV2Block } from '@/blocks/blocks/gamma' +import { GitLabBlock, GitLabV2Block } from '@/blocks/blocks/gitlab' +import { GrainV2Block, GrainV3Block } from '@/blocks/blocks/grain' +import { GranolaBlock, GranolaV2Block } from '@/blocks/blocks/granola' import { HexBlock, HexV2Block } from '@/blocks/blocks/hex' import { HuggingFaceBlock, HuggingFaceV2Block } from '@/blocks/blocks/huggingface' import { IAMBlock, IAMV2Block } from '@/blocks/blocks/iam' @@ -46,12 +54,14 @@ import { IntercomV2Block, IntercomV3Block } from '@/blocks/blocks/intercom' import { LambdaBlock, LambdaV2Block } from '@/blocks/blocks/lambda' import { LangsmithBlock, LangsmithV2Block } from '@/blocks/blocks/langsmith' import { LaunchDarklyBlock, LaunchDarklyV2Block } from '@/blocks/blocks/launchdarkly' +import { LemlistBlock, LemlistV2Block } from '@/blocks/blocks/lemlist' import { LinqBlock, LinqV2Block } from '@/blocks/blocks/linq' import { LoopsBlock, LoopsV2Block } from '@/blocks/blocks/loops' import { LumaBlock, LumaV2Block } from '@/blocks/blocks/luma' import { MailchimpBlock, MailchimpV2Block } from '@/blocks/blocks/mailchimp' import { Mem0Block, Mem0V2Block } from '@/blocks/blocks/mem0' import { OtterBlock, OtterV2Block } from '@/blocks/blocks/otter' +import { PagerDutyBlock, PagerDutyV2Block } from '@/blocks/blocks/pagerduty' import { PersonaBlock, PersonaV2Block } from '@/blocks/blocks/persona' import { ProfoundBlock, ProfoundV2Block } from '@/blocks/blocks/profound' import { PulseV2Block, PulseV3Block } from '@/blocks/blocks/pulse' @@ -60,11 +70,14 @@ import { QuiverV2Block, QuiverV3Block } from '@/blocks/blocks/quiver' import { RB2BBlock, RB2BV2Block } from '@/blocks/blocks/rb2b' import { RDSBlock, RDSV2Block } from '@/blocks/blocks/rds' import { ReductoV2Block, ReductoV3Block } from '@/blocks/blocks/reducto' +import { ResendBlock, ResendV2Block } from '@/blocks/blocks/resend' import { RipplingBlock, RipplingV2Block } from '@/blocks/blocks/rippling' import { RocketlaneBlock, RocketlaneV2Block } from '@/blocks/blocks/rocketlane' +import { RootlyBlock, RootlyV2Block } from '@/blocks/blocks/rootly' import { S3Block, S3V2Block } from '@/blocks/blocks/s3' import { SecretsManagerBlock, SecretsManagerV2Block } from '@/blocks/blocks/secrets_manager' import { SendGridBlock, SendGridV2Block } from '@/blocks/blocks/sendgrid' +import { SentryBlock, SentryV2Block } from '@/blocks/blocks/sentry' import { SESBlock, SESV2Block } from '@/blocks/blocks/ses' import { SimilarwebBlock, SimilarwebV2Block } from '@/blocks/blocks/similarweb' import { SixtyfourBlock, SixtyfourV2Block } from '@/blocks/blocks/sixtyfour' @@ -73,12 +86,17 @@ import { SportmonksBlock, SportmonksV2Block } from '@/blocks/blocks/sportmonks' import { SQSBlock, SQSV2Block } from '@/blocks/blocks/sqs' import { SquareBlock, SquareV2Block } from '@/blocks/blocks/square' import { SSMBlock, SSMV2Block } from '@/blocks/blocks/ssm' +import { StripeBlock, StripeV2Block } from '@/blocks/blocks/stripe' import { STSBlock, STSV2Block } from '@/blocks/blocks/sts' import { TailscaleBlock, TailscaleV2Block } from '@/blocks/blocks/tailscale' import { TavilyBlock, TavilyV2Block } from '@/blocks/blocks/tavily' +import { TelegramBlock, TelegramV2Block } from '@/blocks/blocks/telegram' import { TextractBlock, TextractV2Block, TextractV3Block } from '@/blocks/blocks/textract' import { TriggerDevBlock, TriggerDevV2Block } from '@/blocks/blocks/trigger_dev' +import { TypeformBlock, TypeformV2Block } from '@/blocks/blocks/typeform' import { UptimeRobotBlock, UptimeRobotV2Block } from '@/blocks/blocks/uptimerobot' +import { VercelBlock, VercelV2Block } from '@/blocks/blocks/vercel' +import { WhatsAppBlock, WhatsAppV2Block } from '@/blocks/blocks/whatsapp' import { YouComBlock, YouComV2Block } from '@/blocks/blocks/youcom' import { YouTubeBlock, YouTubeV2Block } from '@/blocks/blocks/youtube' import { ZepBlock, ZepV2Block } from '@/blocks/blocks/zep' @@ -100,6 +118,8 @@ const VERSIONS: Array<{ legacy: BlockConfig successor: BlockConfig secretIds: readonly string[] + /** Replaced connection fields the legacy block never required (a host defaulting to the vendor's). */ + optionalIds?: readonly string[] }> = [ { legacy: AppConfigBlock, @@ -229,6 +249,28 @@ const VERSIONS: Array<{ { legacy: QuiverV2Block, successor: QuiverV3Block, secretIds: ['apiKey'] }, { legacy: ReductoV2Block, successor: ReductoV3Block, secretIds: ['apiKey'] }, { legacy: FlintBlock, successor: FlintV2Block, secretIds: ['apiKey'] }, + { legacy: AshbyBlock, successor: AshbyV2Block, secretIds: ['apiKey'] }, + { legacy: CalendlyBlock, successor: CalendlyV2Block, secretIds: ['apiKey'] }, + { legacy: CheckrBlock, successor: CheckrV2Block, secretIds: ['apiKey'] }, + { legacy: FathomBlock, successor: FathomV2Block, secretIds: ['apiKey'] }, + { legacy: GrainV2Block, successor: GrainV3Block, secretIds: ['apiKey'] }, + { legacy: GranolaBlock, successor: GranolaV2Block, secretIds: ['apiKey'] }, + { legacy: LemlistBlock, successor: LemlistV2Block, secretIds: ['apiKey'] }, + { legacy: PagerDutyBlock, successor: PagerDutyV2Block, secretIds: ['apiKey'] }, + { legacy: ResendBlock, successor: ResendV2Block, secretIds: ['resendApiKey'] }, + { legacy: RootlyBlock, successor: RootlyV2Block, secretIds: ['apiKey'] }, + { legacy: SentryBlock, successor: SentryV2Block, secretIds: ['apiKey'] }, + { legacy: StripeBlock, successor: StripeV2Block, secretIds: ['apiKey'] }, + { legacy: TelegramBlock, successor: TelegramV2Block, secretIds: ['botToken'] }, + { legacy: TypeformBlock, successor: TypeformV2Block, secretIds: ['apiKey'] }, + { legacy: VercelBlock, successor: VercelV2Block, secretIds: ['apiKey'] }, + { legacy: WhatsAppBlock, successor: WhatsAppV2Block, secretIds: ['accessToken'] }, + { + legacy: GitLabBlock, + successor: GitLabV2Block, + secretIds: ['accessToken', 'host'], + optionalIds: ['host'], + }, ] function valuesFor(ids: readonly string[], prefix: string): Record { @@ -250,7 +292,7 @@ function firstOperation(config: BlockConfig): string | undefined { function fieldTitles(config: BlockConfig, ids: readonly string[]): string[] { return config.subBlocks - .filter((subBlock) => ids.includes(subBlock.id)) + .filter((subBlock) => ids.includes(subBlock.id) && !isTriggerModeSubBlock(subBlock)) .map((subBlock) => subBlock.title ?? subBlock.id) } @@ -280,7 +322,7 @@ function missingRequiredFields(block: BlockState): string { } } -describe.each(VERSIONS)('$legacy.type legacy block', ({ legacy, secretIds }) => { +describe.each(VERSIONS)('$legacy.type legacy block', ({ legacy, secretIds, optionalIds = [] }) => { beforeEach(() => { vi.spyOn(blocks, 'getBlock').mockReturnValue(legacy) }) @@ -293,10 +335,11 @@ describe.each(VERSIONS)('$legacy.type legacy block', ({ legacy, secretIds }) => }) it('still requires its inline secrets', () => { + const requiredIds = secretIds.filter((id) => !optionalIds.includes(id)) const message = missingRequiredFields(buildBlock(legacy, {})) - const titles = fieldTitles(legacy, secretIds) + const titles = fieldTitles(legacy, requiredIds) - expect(titles).toHaveLength(secretIds.length) + expect(titles).toHaveLength(requiredIds.length) for (const title of titles) expect(message).toContain(title) }) }) diff --git a/apps/sim/tools/ashby/add_candidate_tag.ts b/apps/sim/tools/ashby/add_candidate_tag.ts index b4c6b33c25d..2ae510c7ae5 100644 --- a/apps/sim/tools/ashby/add_candidate_tag.ts +++ b/apps/sim/tools/ashby/add_candidate_tag.ts @@ -6,6 +6,7 @@ import { CANDIDATE_OUTPUTS, mapCandidate, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyAddCandidateTagParams { @@ -27,6 +28,7 @@ export const addCandidateTagTool: ToolConfig< name: 'Ashby Add Candidate Tag', description: 'Adds a tag to a candidate in Ashby and returns the updated candidate.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/anonymize_candidate.ts b/apps/sim/tools/ashby/anonymize_candidate.ts index c121c3c916c..bdae7b6504e 100644 --- a/apps/sim/tools/ashby/anonymize_candidate.ts +++ b/apps/sim/tools/ashby/anonymize_candidate.ts @@ -6,6 +6,7 @@ import { CANDIDATE_OUTPUTS, mapCandidate, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyAnonymizeCandidateParams { @@ -27,6 +28,7 @@ export const anonymizeCandidateTool: ToolConfig< description: 'Remove personally identifiable information from an Ashby candidate while retaining the candidate record and its applications. This operation does not delete the candidate. Requires the candidatesWrite permission.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/change_application_source.ts b/apps/sim/tools/ashby/change_application_source.ts index 7efbdb97223..3142b763f24 100644 --- a/apps/sim/tools/ashby/change_application_source.ts +++ b/apps/sim/tools/ashby/change_application_source.ts @@ -6,6 +6,7 @@ import { ashbyErrorMessage, mapApplication, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyChangeApplicationSourceParams { @@ -29,6 +30,7 @@ export const changeApplicationSourceTool: ToolConfig< description: 'Changes the source attributed to an existing application, so programmatically created applications report correctly on the recruiting side. Requires the candidatesWrite permission.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/change_application_stage.ts b/apps/sim/tools/ashby/change_application_stage.ts index ef15d5a4ed4..6a87546ed34 100644 --- a/apps/sim/tools/ashby/change_application_stage.ts +++ b/apps/sim/tools/ashby/change_application_stage.ts @@ -7,6 +7,7 @@ import { ashbyIsoDateTime, mapApplication, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyChangeApplicationStageParams { @@ -34,6 +35,7 @@ export const changeApplicationStageTool: ToolConfig< description: 'Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/create_application.ts b/apps/sim/tools/ashby/create_application.ts index 0d473ade5ad..7e4674e29e1 100644 --- a/apps/sim/tools/ashby/create_application.ts +++ b/apps/sim/tools/ashby/create_application.ts @@ -6,6 +6,7 @@ import { ashbyErrorMessage, mapApplication, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyCreateApplicationParams { @@ -34,6 +35,7 @@ export const createApplicationTool: ToolConfig< description: 'Creates a new application for a candidate on a job. Optionally specify interview plan, stage, source, and credited user.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/create_candidate.ts b/apps/sim/tools/ashby/create_candidate.ts index 6cfdedffcb4..a72e3035126 100644 --- a/apps/sim/tools/ashby/create_candidate.ts +++ b/apps/sim/tools/ashby/create_candidate.ts @@ -6,6 +6,7 @@ import { CANDIDATE_OUTPUTS, mapCandidate, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const createCandidateTool: ToolConfig< @@ -16,6 +17,7 @@ export const createCandidateTool: ToolConfig< name: 'Ashby Create Candidate', description: 'Creates a new candidate record in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/create_note.ts b/apps/sim/tools/ashby/create_note.ts index 7a41fcf62d6..ca2b337bd3f 100644 --- a/apps/sim/tools/ashby/create_note.ts +++ b/apps/sim/tools/ashby/create_note.ts @@ -1,5 +1,6 @@ import type { AshbyCreateNoteParams, AshbyCreateNoteResponse } from '@/tools/ashby/types' import { ASHBY_ON_BEHALF_OF_PARAM, ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const createNoteTool: ToolConfig = { @@ -8,6 +9,7 @@ export const createNoteTool: ToolConfig = { @@ -12,6 +13,7 @@ export const getCandidateTool: ToolConfig = { @@ -7,6 +8,7 @@ export const getJobTool: ToolConfig = { name: 'Ashby Get Job', description: 'Retrieves full details about a single job by its ID.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/get_job_posting.ts b/apps/sim/tools/ashby/get_job_posting.ts index 2b47a46853a..c66b420d2ce 100644 --- a/apps/sim/tools/ashby/get_job_posting.ts +++ b/apps/sim/tools/ashby/get_job_posting.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyGetJobPostingParams { @@ -88,6 +89,7 @@ export const getJobPostingTool: ToolConfig = { name: 'Ashby Get Opening', description: 'Retrieves one Ashby headcount opening by UUID.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/list_application_feedback.ts b/apps/sim/tools/ashby/list_application_feedback.ts index 869a8dda946..9a6e1bfa56d 100644 --- a/apps/sim/tools/ashby/list_application_feedback.ts +++ b/apps/sim/tools/ashby/list_application_feedback.ts @@ -5,6 +5,7 @@ import { ashbyTimestamp, USER_SUMMARY_OUTPUT, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -29,6 +30,7 @@ export const listApplicationFeedbackTool: ToolConfig = { description: 'Lists submitted interview feedback, optionally for one application, with pagination and incremental sync.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/list_application_history.ts b/apps/sim/tools/ashby/list_application_history.ts index 9843321b7b3..4349a666592 100644 --- a/apps/sim/tools/ashby/list_application_history.ts +++ b/apps/sim/tools/ashby/list_application_history.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage, ashbyLimit } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -16,6 +17,7 @@ export const listApplicationHistoryTool: ToolConfig = { name: 'Ashby List Application History', description: 'Lists the full stage history and allowed actions for an application.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/list_applications.ts b/apps/sim/tools/ashby/list_applications.ts index edaac4d7a8a..23b3424b0b7 100644 --- a/apps/sim/tools/ashby/list_applications.ts +++ b/apps/sim/tools/ashby/list_applications.ts @@ -10,6 +10,7 @@ import { ashbyTimestamp, mapApplication, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const listApplicationsTool: ToolConfig< @@ -21,6 +22,7 @@ export const listApplicationsTool: ToolConfig< description: 'Lists all applications in an Ashby organization with pagination and optional filters for status, job, and creation date.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_archive_reasons.ts b/apps/sim/tools/ashby/list_archive_reasons.ts index 021468d04c8..9734896a790 100644 --- a/apps/sim/tools/ashby/list_archive_reasons.ts +++ b/apps/sim/tools/ashby/list_archive_reasons.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListArchiveReasonsParams { @@ -27,6 +28,7 @@ export const listArchiveReasonsTool: ToolConfig< name: 'Ashby List Archive Reasons', description: 'Lists all archive reasons configured in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_candidate_tags.ts b/apps/sim/tools/ashby/list_candidate_tags.ts index 086e26af392..174a2c1db25 100644 --- a/apps/sim/tools/ashby/list_candidate_tags.ts +++ b/apps/sim/tools/ashby/list_candidate_tags.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage, ashbyLimit } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListCandidateTagsParams { @@ -32,6 +33,7 @@ export const listCandidateTagsTool: ToolConfig< name: 'Ashby List Candidate Tags', description: 'Lists all candidate tags configured in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_candidates.ts b/apps/sim/tools/ashby/list_candidates.ts index 8b1ce88848e..a9d213f19c2 100644 --- a/apps/sim/tools/ashby/list_candidates.ts +++ b/apps/sim/tools/ashby/list_candidates.ts @@ -7,6 +7,7 @@ import { CANDIDATE_OUTPUTS, mapCandidate, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const listCandidatesTool: ToolConfig< @@ -17,6 +18,7 @@ export const listCandidatesTool: ToolConfig< name: 'Ashby List Candidates', description: 'Lists all candidates in an Ashby organization with cursor-based pagination.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_custom_fields.ts b/apps/sim/tools/ashby/list_custom_fields.ts index 640117c5e4d..bf5bb84df1a 100644 --- a/apps/sim/tools/ashby/list_custom_fields.ts +++ b/apps/sim/tools/ashby/list_custom_fields.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage, ashbyLimit } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListCustomFieldsParams { @@ -41,6 +42,7 @@ export const listCustomFieldsTool: ToolConfig< name: 'Ashby List Custom Fields', description: 'Lists all custom field definitions configured in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_departments.ts b/apps/sim/tools/ashby/list_departments.ts index e9db6370654..eeeba817b00 100644 --- a/apps/sim/tools/ashby/list_departments.ts +++ b/apps/sim/tools/ashby/list_departments.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage, ashbyLimit } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListDepartmentsParams { @@ -37,6 +38,7 @@ export const listDepartmentsTool: ToolConfig< name: 'Ashby List Departments', description: 'Lists all departments in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_interview_plans.ts b/apps/sim/tools/ashby/list_interview_plans.ts index e95ffcb4e33..689472558b9 100644 --- a/apps/sim/tools/ashby/list_interview_plans.ts +++ b/apps/sim/tools/ashby/list_interview_plans.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage, ashbyLimit } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -29,6 +30,7 @@ export const listInterviewPlansTool: ToolConfig = { description: 'Lists Ashby interview plans, including optional archived plans and incremental changes.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/list_interview_stages.ts b/apps/sim/tools/ashby/list_interview_stages.ts index 67a0aae6d76..4d39357c93d 100644 --- a/apps/sim/tools/ashby/list_interview_stages.ts +++ b/apps/sim/tools/ashby/list_interview_stages.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -23,6 +24,7 @@ export const listInterviewStagesTool: ToolConfig = { name: 'Ashby List Interview Stages', description: 'Lists the ordered stages in an Ashby interview plan.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/list_interviews.ts b/apps/sim/tools/ashby/list_interviews.ts index a64b0ca4e82..8284bfe2d5a 100644 --- a/apps/sim/tools/ashby/list_interviews.ts +++ b/apps/sim/tools/ashby/list_interviews.ts @@ -7,6 +7,7 @@ import { mapUserSummary, USER_SUMMARY_OUTPUT, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListInterviewSchedulesParams { @@ -104,6 +105,7 @@ export const listInterviewsTool: ToolConfig< description: 'Lists interview schedules in Ashby, optionally filtered by application or interview stage.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_job_postings.ts b/apps/sim/tools/ashby/list_job_postings.ts index f4321116220..5f96e28a3c1 100644 --- a/apps/sim/tools/ashby/list_job_postings.ts +++ b/apps/sim/tools/ashby/list_job_postings.ts @@ -1,4 +1,5 @@ import { ashbyAuthHeaders, ashbyErrorMessage } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbyListJobPostingsParams { @@ -48,6 +49,7 @@ export const listJobPostingsTool: ToolConfig< name: 'Ashby List Job Postings', description: 'Lists all job postings in Ashby.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/list_jobs.ts b/apps/sim/tools/ashby/list_jobs.ts index 9c718796540..c3dc77937c5 100644 --- a/apps/sim/tools/ashby/list_jobs.ts +++ b/apps/sim/tools/ashby/list_jobs.ts @@ -7,6 +7,7 @@ import { JOB_OUTPUTS, mapJob, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' /** Normalize the array schema plus legacy scalar and JSON-string status inputs. */ @@ -38,6 +39,7 @@ export const listJobsTool: ToolConfig = { description: 'Searches Ashby jobs by title and/or requisition ID. Provide at least one of these filters.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/search_openings.ts b/apps/sim/tools/ashby/search_openings.ts index a8537b94b89..ce179d0d4ce 100644 --- a/apps/sim/tools/ashby/search_openings.ts +++ b/apps/sim/tools/ashby/search_openings.ts @@ -5,6 +5,7 @@ import { mapOpenings, OPENINGS_OUTPUT, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -19,6 +20,7 @@ export const searchOpeningsTool: ToolConfig = { name: 'Ashby Search Openings', description: 'Searches Ashby headcount openings by human-readable identifier.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/search_users.ts b/apps/sim/tools/ashby/search_users.ts index 2baddfc4936..611b32d5397 100644 --- a/apps/sim/tools/ashby/search_users.ts +++ b/apps/sim/tools/ashby/search_users.ts @@ -5,6 +5,7 @@ import { mapUserSummary, USER_SUMMARY_OUTPUT, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -19,6 +20,7 @@ export const searchUsersTool: ToolConfig = { name: 'Ashby Search Users', description: 'Searches Ashby users by exact email address.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/set_custom_field_value.ts b/apps/sim/tools/ashby/set_custom_field_value.ts index 92469729f31..da0c28c83dc 100644 --- a/apps/sim/tools/ashby/set_custom_field_value.ts +++ b/apps/sim/tools/ashby/set_custom_field_value.ts @@ -7,6 +7,7 @@ import { mapCustomFieldOnObject, normalizeObjectType, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbySetCustomFieldValueParams { @@ -33,6 +34,7 @@ export const setCustomFieldValueTool: ToolConfig< description: 'Sets the value of a single custom field on an Ashby Application, Candidate, Job, or Opening. Custom fields are the only way to annotate a job or req, since Ashby has no job notes and no job tags. Requires the candidatesWrite permission.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/set_custom_field_values.ts b/apps/sim/tools/ashby/set_custom_field_values.ts index 2c3052086c4..66841e38fb7 100644 --- a/apps/sim/tools/ashby/set_custom_field_values.ts +++ b/apps/sim/tools/ashby/set_custom_field_values.ts @@ -7,6 +7,7 @@ import { mapCustomFieldOnObject, normalizeObjectType, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface AshbySetCustomFieldValuesParams { @@ -32,6 +33,7 @@ export const setCustomFieldValuesTool: ToolConfig< description: 'Sets several custom field values on one Ashby Application, Candidate, Job, or Opening in a single call. Prefer this over repeated single-field writes to the same object - Ashby recommends it because concurrent single-field calls can race and overwrite each other. Requires the candidatesWrite permission.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/transfer_application.ts b/apps/sim/tools/ashby/transfer_application.ts index cf9f8dd7b7a..7189efa785d 100644 --- a/apps/sim/tools/ashby/transfer_application.ts +++ b/apps/sim/tools/ashby/transfer_application.ts @@ -5,6 +5,7 @@ import { ashbyErrorMessage, mapApplication, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig, ToolResponse } from '@/tools/types' interface Params { @@ -24,6 +25,7 @@ export const transferApplicationTool: ToolConfig = { name: 'Ashby Transfer Application', description: 'Transfers an application to another job, interview plan, and stage.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { type: 'string', diff --git a/apps/sim/tools/ashby/update_candidate.ts b/apps/sim/tools/ashby/update_candidate.ts index 5e3b1207503..fbe2bf954ff 100644 --- a/apps/sim/tools/ashby/update_candidate.ts +++ b/apps/sim/tools/ashby/update_candidate.ts @@ -7,6 +7,7 @@ import { CANDIDATE_OUTPUTS, mapCandidate, } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' interface AshbyUpdateCandidateParams { @@ -38,6 +39,7 @@ export const updateCandidateTool: ToolConfig< name: 'Ashby Update Candidate', description: 'Updates an existing candidate record in Ashby. Only provided fields are changed.', version: '1.0.0', + oauth: ASHBY_CREDENTIAL_OAUTH, params: { apiKey: { diff --git a/apps/sim/tools/ashby/upload_candidate_file.ts b/apps/sim/tools/ashby/upload_candidate_file.ts index b5d3b931eef..8fe853c048d 100644 --- a/apps/sim/tools/ashby/upload_candidate_file.ts +++ b/apps/sim/tools/ashby/upload_candidate_file.ts @@ -1,5 +1,6 @@ import type { AshbyGetCandidateResponse } from '@/tools/ashby/types' import { CANDIDATE_OUTPUTS } from '@/tools/ashby/utils' +import { ASHBY_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { InternalToolConfig } from '@/tools/types' interface Params { @@ -14,6 +15,7 @@ export const uploadCandidateFileTool: InternalToolConfig = { @@ -7,6 +8,7 @@ export const cancelEventTool: ToolConfig = { @@ -7,6 +8,7 @@ export const getUserTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrAddReportTagTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrCompleteReportTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrCreateGeoTool: ToolConfig = { @@ -23,6 +24,7 @@ export const checkrCreateReportTool: ToolConfig = { @@ -13,6 +14,7 @@ export const checkrDeleteGeoTool: ToolConfig = { @@ -15,6 +16,7 @@ export const checkrGetAccountTool: ToolConfig = @@ -19,6 +20,7 @@ export const checkrGetCandidateTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrGetDocumentTool: ToolConfig = { @@ -18,6 +19,7 @@ export const checkrGetGeoTool: ToolConfig name: 'Checkr Get Geo', description: 'Retrieve a geo by ID.', version: '1.0.0', + oauth: CHECKR_CREDENTIAL_OAUTH, errorExtractor: CHECKR_ERROR_EXTRACTOR, params: { diff --git a/apps/sim/tools/checkr/get_invitation.ts b/apps/sim/tools/checkr/get_invitation.ts index 2e4b2f6caba..7c038f094c1 100644 --- a/apps/sim/tools/checkr/get_invitation.ts +++ b/apps/sim/tools/checkr/get_invitation.ts @@ -11,6 +11,7 @@ import { checkrUrl, mapInvitation, } from '@/tools/checkr/utils' +import { CHECKR_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const checkrGetInvitationTool: ToolConfig< @@ -21,6 +22,7 @@ export const checkrGetInvitationTool: ToolConfig< name: 'Checkr Get Invitation', description: 'Retrieve an invitation by ID, including its status and invitation URL.', version: '1.0.0', + oauth: CHECKR_CREDENTIAL_OAUTH, errorExtractor: CHECKR_ERROR_EXTRACTOR, params: { diff --git a/apps/sim/tools/checkr/get_node.ts b/apps/sim/tools/checkr/get_node.ts index 2935717f540..13174f901f7 100644 --- a/apps/sim/tools/checkr/get_node.ts +++ b/apps/sim/tools/checkr/get_node.ts @@ -11,6 +11,7 @@ import { checkrUrl, mapNode, } from '@/tools/checkr/utils' +import { CHECKR_CREDENTIAL_OAUTH } from '@/tools/shared/saved-credentials' import type { ToolConfig } from '@/tools/types' export const checkrGetNodeTool: ToolConfig = { @@ -18,6 +19,7 @@ export const checkrGetNodeTool: ToolConfig = { @@ -18,6 +19,7 @@ export const checkrGetPackageTool: ToolConfig = { @@ -18,6 +19,7 @@ export const checkrGetProgramTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrGetReportTool: ToolConfig = @@ -14,6 +15,7 @@ export const checkrGetReportEtaTool: ToolConfig = { @@ -18,6 +19,7 @@ export const checkrGetReportTagsTool: ToolConfig = { @@ -22,6 +23,7 @@ export const checkrListGeosTool: ToolConfig = { @@ -22,6 +23,7 @@ export const checkrListNodesTool: ToolConfig = { @@ -21,6 +22,7 @@ export const checkrListUsersTool: ToolConfig = { @@ -19,6 +20,7 @@ export const checkrUpdateGeoTool: ToolConfig = { @@ -20,6 +21,7 @@ export const checkrUpdateReportTool: ToolConfig = { @@ -6,6 +7,7 @@ export const getSummaryTool: ToolConfig = @@ -7,6 +8,7 @@ export const getTranscriptTool: ToolConfig = { @@ -6,6 +7,7 @@ export const listMeetingsTool: ToolConfig = { @@ -6,6 +7,7 @@ export const listTeamsTool: ToolConfig = JSON.parse( - '{"a2a_cancel_task":{"id":"a2a_cancel_task","name":"A2A Cancel Task","description":"Request cancellation of an in-progress A2A task.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The task ID to cancel"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_get_agent_card":{"id":"a2a_get_agent_card","name":"A2A Get Agent Card","description":"Fetch the Agent Card (discovery document) for an external A2A agent.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_get_task":{"id":"a2a_get_task","name":"A2A Get Task","description":"Retrieve the current state and result of an A2A task.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The task ID to retrieve"},"historyLength":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of history messages to include"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_send_message":{"id":"a2a_send_message","name":"A2A Send Message","description":"Send a message to an external A2A agent and return its response.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"message":{"type":"string","required":true,"visibility":"user-or-llm","description":"The message text to send"},"data":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional structured JSON data to attach"},"files":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional files to attach"},"taskId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Existing task ID to continue"},"contextId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Conversation context ID to continue"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"affinity_batch_update_entity_fields":{"id":"affinity_batch_update_entity_fields","name":"Affinity Batch Update Entity Fields","description":"Write up to 100 non-list field values on one company or person in a single request.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to write the fields on: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"updates":{"type":"json","required":true,"visibility":"user-or-llm","description":"Up to 100 field updates as [{\\"id\\":\\"\\",\\"value\\":{\\"type\\":\\"…\\",\\"data\\":…}}], using the same value shapes as a single field update"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_batch_update_list_entry_fields":{"id":"affinity_batch_update_list_entry_fields","name":"Affinity Batch Update List Entry Fields","description":"Write up to 100 field values on one list row in a single request. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"updates":{"type":"json","required":true,"visibility":"user-or-llm","description":"Up to 100 field updates as [{\\"id\\":\\"\\",\\"value\\":{\\"type\\":\\"…\\",\\"data\\":…}}], using the same value shapes as a single field update"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_list":{"id":"affinity_create_list","name":"Affinity Create List","description":"Create a list. Its type fixes which entities it can hold, and the API key holder becomes its creator and owner.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the new list"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Entity kind the list holds: company, opportunity, or person"},"isPublic":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether everyone in the organization can see the list"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_list_field_dropdown_option":{"id":"affinity_create_list_field_dropdown_option","name":"Affinity Create List Field Dropdown Option","description":"Add a selectable option to a dropdown field on a list. A ranked or status option also needs a rank and a color.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Kind of option to create, matching the field. dropdown takes only a label; ranked-dropdown also requires rank and color; status-dropdown additionally requires a status category. Sending a field the kind does not accept is rejected"},"text":{"type":"string","required":true,"visibility":"user-or-llm","description":"The option label"},"rank":{"type":"number","required":false,"visibility":"user-or-llm","description":"Sort order. Required on a ranked-dropdown or status-dropdown option"},"color":{"type":"string","required":false,"visibility":"user-or-llm","description":"Option color: white, gray, blue, green, purple, orange, or red. Required on a ranked-dropdown or status-dropdown option"},"statusCategory":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pipeline meaning of the option: open, won, lost, or on-hold. Status-dropdown options only"},"winRate":{"type":"number","required":false,"visibility":"user-or-llm","description":"Expected win rate of the status. Status-dropdown options only"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_merge":{"id":"affinity_create_merge","name":"Affinity Create Merge","description":"Fold a duplicate company or person into the record you are keeping. The merge runs asynchronously — poll the returned task to see it finish. Requires the \\"Manage duplicates\\" permission and an admin role.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to merge: companies or persons"},"primaryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to keep"},"duplicateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the duplicate record to fold in"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_note":{"id":"affinity_create_note","name":"Affinity Create Note","description":"Write a note — attached to companies, persons, and opportunities, anchored to a meeting, call, or chat message, or posted as a reply to an existing note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Note shape: entities to attach it to records, interaction to anchor it to a meeting, call, or chat message, or user-reply to reply to a note"},"html":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note body as HTML"},"companyIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Companies to attach the note to, e.g. [1, 2]. Not used on a reply"},"personIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Persons to attach the note to, e.g. [1, 2]. Not used on a reply"},"opportunityIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Opportunities to attach the note to, e.g. [1, 2]. Not used on a reply"},"interactionId":{"type":"string","required":false,"visibility":"user-or-llm","description":"The interaction to anchor the note to. Required for an interaction note"},"interactionType":{"type":"string","required":false,"visibility":"user-or-llm","description":"Kind of the anchoring interaction: meeting, call, or chat-message. Required for an interaction note"},"parentId":{"type":"string","required":false,"visibility":"user-or-llm","description":"The note being replied to. Required for a user-reply note"},"creatorId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Attribute the note to another internal person. Defaults to the API key holder"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"Backdate the note to this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_reminder":{"id":"affinity_create_reminder","name":"Affinity Create Reminder","description":"Create a reminder on one company, person, or opportunity. A recurring reminder resets whenever the chosen signal happens instead of firing once.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"one-time to fire once, or recurring to reset on a signal"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"What the reminder is about: company, person, or opportunity"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company, person, or opportunity"},"dueDate":{"type":"string","required":false,"visibility":"user-or-llm","description":"When the reminder is due, as an ISO 8601 timestamp. Required for a one-time reminder; on a recurring one Affinity computes it from the period when omitted"},"content":{"type":"string","required":false,"visibility":"user-or-llm","description":"What the reminder says"},"ownerId":{"type":"string","required":true,"visibility":"user-or-llm","description":"User the reminder is assigned to. Must be an internal user. The API key holder is recorded as the creator, which is a separate field"},"resetTrigger":{"type":"string","required":false,"visibility":"user-or-llm","description":"What restarts a recurring reminder: interaction, email, or event. Required when the type is recurring"},"periodDays":{"type":"number","required":false,"visibility":"user-or-llm","description":"Days between firings of a recurring reminder. Required when the type is recurring"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_delete_list_field_dropdown_option":{"id":"affinity_delete_list_field_dropdown_option","name":"Affinity Delete List Field Dropdown Option","description":"Permanently delete a dropdown option on a list field. Every list entry currently set to it is cleared, and those values cannot be recovered.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID to delete"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_delete_note":{"id":"affinity_delete_note","name":"Affinity Delete Note","description":"Delete a note you created. Deleting a root note also deletes its replies; deleting a reply removes only that reply.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID to delete"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_company":{"id":"affinity_get_company","name":"Affinity Get Company","description":"Look up one company by ID. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"companyId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The company ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_current_user":{"id":"affinity_get_current_user","name":"Affinity Get Current User","description":"Verify an Affinity API key and return the tenant, the user behind the key, and the scopes the grant carries.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_entity_field_value":{"id":"affinity_get_entity_field_value","name":"Affinity Get Entity Field Value","description":"Read one non-list field value from a company or person.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to read the field from: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to read"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list":{"id":"affinity_get_list","name":"Affinity Get List","description":"Read one list — its name, type, owner, and privacy setting.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_entry":{"id":"affinity_get_list_entry","name":"Affinity Get List Entry","description":"Read one row of a list with its entity. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_entry_field":{"id":"affinity_get_list_entry_field","name":"Affinity Get List Entry Field","description":"Read one field value on a list row.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to read"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_field_dropdown_option":{"id":"affinity_get_list_field_dropdown_option","name":"Affinity Get List Field Dropdown Option","description":"Read one dropdown option on a list field.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_merge":{"id":"affinity_get_merge","name":"Affinity Get Merge","description":"Read the status of one company or person merge, including why it failed if it did.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge to read: companies or persons"},"mergeId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The merge ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_merge_task":{"id":"affinity_get_merge_task","name":"Affinity Get Merge Task","description":"Read one merge task and how its merges are progressing. Poll this after starting a merge.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge task to read: companies or persons"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The merge task ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_note":{"id":"affinity_get_note","name":"Affinity Get Note","description":"Read one note with its body, author, mentions, and attached records.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return, e.g. [\\"repliesCount\\",\\"personsPreview\\",\\"companiesPreview\\",\\"opportunitiesPreview\\"]. Those four fields are omitted unless requested here"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_opportunity":{"id":"affinity_get_opportunity","name":"Affinity Get Opportunity","description":"Read one opportunity and the list it belongs to. Its field data lives on the list entry.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"opportunityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The opportunity ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_person":{"id":"affinity_get_person","name":"Affinity Get Person","description":"Look up one person by ID. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"personId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The person ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_saved_view":{"id":"affinity_get_saved_view","name":"Affinity Get Saved View","description":"Read one saved view — its name, kind, and creation date.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"viewId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The saved view ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_transcript":{"id":"affinity_get_transcript","name":"Affinity Get Transcript","description":"Read one transcript with its first 100 fragments. Page the fragments endpoint for a longer meeting.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"transcriptId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The transcript ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_user":{"id":"affinity_get_user","name":"Affinity Get User","description":"Read one internal user. A user and their person record share the same numeric ID, so a person ID works here.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"userId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The user ID, which is also their person ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_calls":{"id":"affinity_list_calls","name":"Affinity List Calls","description":"Page through logged calls and their participants. Only calls the API key holder can see are returned.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_chat_messages":{"id":"affinity_list_chat_messages","name":"Affinity List Chat Messages","description":"Page through logged chat messages and their participants. Only messages the API key holder can see are returned.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_companies":{"id":"affinity_list_companies","name":"Affinity List Companies","description":"Page through companies. Companies come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these company IDs, e.g. [1, 2, 3]"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_coworker_connections":{"id":"affinity_list_coworker_connections","name":"Affinity List Coworker Connections","description":"Find warm paths into a company through shared work history: who in your Affinity data once worked alongside the people you want to reach. Grouped by target, strongest first.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":true,"visibility":"user-or-llm","description":"Required scope. The only supported filter is target.currentCompany.id, e.g. \\"target.currentCompany.id=123\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of targets to return per page, 1-50. Defaults to 20"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_emails":{"id":"affinity_list_emails","name":"Affinity List Emails","description":"Page through email metadata — subject, participants, and timestamps. Affinity never exposes email bodies through the API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_field_values":{"id":"affinity_list_entity_field_values","name":"Affinity List Entity Field Values","description":"Page through a company\'s or person\'s non-list field values. List fields are not returned here — read those through the list entry.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to read field values from: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field IDs. Mutually exclusive with Field Types"},"types":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field categories: enriched, global, relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_list_entries":{"id":"affinity_list_entity_list_entries","name":"Affinity List Entity List Entries","description":"Page through a company\'s or person\'s rows across every list, each carrying that list\'s field values and when the entity was added.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up the rows of: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_lists":{"id":"affinity_list_entity_lists","name":"Affinity List Entity Lists","description":"List every list a company or person appears on that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up the lists of: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_notes":{"id":"affinity_list_entity_notes","name":"Affinity List Entity Notes","description":"List the notes relevant to one company, person, or opportunity — directly attached notes plus notes reaching it through its people and meetings.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity the notes hang off: companies, persons, or opportunities"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company, person, or opportunity"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_relationships":{"id":"affinity_list_entity_relationships","name":"Affinity List Entity Relationships","description":"List who knows a company or person, scored 0.0 to 1.0 by how much the two actually interact. Strongest first by default.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up relationships for: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on interactionScore only, e.g. \\"interactionScore>=0.5\\""},"orderBy":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order: [\\"interactionScore\\"] for weakest first, [\\"-interactionScore\\"] for strongest first (the default)"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_dropdown_options":{"id":"affinity_list_field_dropdown_options","name":"Affinity List Field Dropdown Options","description":"List the selectable options on a dropdown or ranked-dropdown company or person field. Writing such a field needs the option ID, not its text.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which field family the field belongs to: companies or persons"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown or ranked-dropdown field ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_metadata":{"id":"affinity_list_field_metadata","name":"Affinity List Field Metadata","description":"List the non-list company or person fields, with the value type, filter operators, and sort support of each. Start here to find the Field IDs the read and write tools take.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which fields to describe: companies or persons"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return: [\\"filterability\\",\\"sortability\\"]. Both are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on name only, e.g. \\"name=~Status\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_value_changes":{"id":"affinity_list_field_value_changes","name":"Affinity List Field Value Changes","description":"Page through field value changes across the whole workspace. Built for delta sync: follow nextCursor to the end of a run, then resume from the last cursor next time.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over field.id, listEntry.id, changer.id, changedAt, or actionType. Resume a sync with e.g. \\"changedAt>2026-06-01T12:00:00Z\\""},"orderBy":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order: [\\"changedAt\\"] for oldest first (the default), [\\"-changedAt\\"] for newest first"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_investor_executive_connections":{"id":"affinity_list_investor_executive_connections","name":"Affinity List Investor Executive Connections","description":"Find warm paths into a company through investment history: which investors in your Affinity data backed a company the people you want to reach once led. Grouped by target, strongest first.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":true,"visibility":"user-or-llm","description":"Required scope. The only supported filter is target.currentCompany.id, e.g. \\"target.currentCompany.id=123\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of targets to return per page, 1-50. Defaults to 20"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entries":{"id":"affinity_list_list_entries","name":"Affinity List List Entries","description":"Page through the rows of a list. Rows come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entry_field_value_changes":{"id":"affinity_list_list_entry_field_value_changes","name":"Affinity List List Entry Field Value Changes","description":"Page through the history of one list row — who changed which field, when, and to what.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over field.id, changer.id, changedAt, or actionType, e.g. \\"field.id=field-1234\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entry_fields":{"id":"affinity_list_list_entry_fields","name":"Affinity List List Entry Fields","description":"Page through every field value on one list row, including the list-specific columns. All fields are returned unless narrowed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field IDs. Mutually exclusive with Field Types"},"types":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field categories: enriched, global, list, relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_field_dropdown_options":{"id":"affinity_list_list_field_dropdown_options","name":"Affinity List List Field Dropdown Options","description":"List the selectable options on a dropdown, ranked-dropdown, or status-dropdown field of a list.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_fields":{"id":"affinity_list_list_fields","name":"Affinity List List Fields","description":"List the fields available on one list, including its list-specific columns. Use these Field IDs when reading or writing list entries.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return: [\\"filterability\\",\\"sortability\\"]. Both are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on name only, e.g. \\"name=~Stage\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_lists":{"id":"affinity_list_lists","name":"Affinity List Lists","description":"Page through the lists in the organization that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"term":{"type":"string","required":false,"visibility":"user-or-llm","description":"Case-insensitive substring match on the list name"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_meetings":{"id":"affinity_list_meetings","name":"Affinity List Meetings","description":"Page through past and upcoming meetings with their organizer and attendees.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_merge_tasks":{"id":"affinity_list_merge_tasks","name":"Affinity List Merge Tasks","description":"Page through merge tasks, each summarizing how many of its merges are in progress, succeeded, or failed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge tasks to list: companies or persons"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on status only, e.g. \\"status=in-progress\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_merges":{"id":"affinity_list_merges","name":"Affinity List Merges","description":"Page through the company or person merges the organization has run, with the status and the records involved in each.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merges to list: companies or persons"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over status or taskId, e.g. \\"status=failed\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_companies":{"id":"affinity_list_note_attached_companies","name":"Affinity List Note Attached Companies","description":"List the companies directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_opportunities":{"id":"affinity_list_note_attached_opportunities","name":"Affinity List Note Attached Opportunities","description":"List the opportunities directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_persons":{"id":"affinity_list_note_attached_persons","name":"Affinity List Note Attached Persons","description":"List the persons directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_replies":{"id":"affinity_list_note_replies","name":"Affinity List Note Replies","description":"Page through the replies on one note, including AI Notetaker replies.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID whose replies to read"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_notes":{"id":"affinity_list_notes","name":"Affinity List Notes","description":"Page through every note the caller can see. Replies are excluded.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return, e.g. [\\"repliesCount\\",\\"personsPreview\\",\\"companiesPreview\\",\\"opportunitiesPreview\\"]. Those four fields are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_opportunities":{"id":"affinity_list_opportunities","name":"Affinity List Opportunities","description":"Page through opportunities. Field data lives on the list entry, not here — read it through the list or saved view the opportunity belongs to.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these opportunity IDs, e.g. [1, 2, 3]"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_persons":{"id":"affinity_list_persons","name":"Affinity List Persons","description":"Page through persons. Persons come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these person IDs, e.g. [1, 2, 3]"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_reminders":{"id":"affinity_list_reminders","name":"Affinity List Reminders","description":"Page through the reminders the caller can see. Filter by status to surface what is overdue.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_saved_view_entries":{"id":"affinity_list_saved_view_entries","name":"Affinity List Saved View Entries","description":"Page through the rows of a saved view. The view\'s own filters and columns decide which rows and which field data come back.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"viewId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The saved view ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_saved_views":{"id":"affinity_list_saved_views","name":"Affinity List Saved Views","description":"List the saved views on a list that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_transcript_fragments":{"id":"affinity_list_transcript_fragments","name":"Affinity List Transcript Fragments","description":"Page through everything said in a meeting, segment by segment with the speaker.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"transcriptId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The transcript ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_transcripts":{"id":"affinity_list_transcripts","name":"Affinity List Transcripts","description":"Page through meeting transcript metadata. Read one transcript to get what was actually said.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_users":{"id":"affinity_list_users","name":"Affinity List Users","description":"Page through the internal users in the organization. Email addresses and roles are returned only to callers with the \\"Manage Users\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"term":{"type":"string","required":false,"visibility":"user-or-llm","description":"Case-insensitive match across first name, last name, and primary email"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over id or status, e.g. \\"status=active\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_companies":{"id":"affinity_search_companies","name":"Affinity Search Companies","description":"Search companies by filters, sorts, and a free-text term. Requires the \\"Export All Organizations directory\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_files":{"id":"affinity_search_files","name":"Affinity Search Files","description":"Search files by keyword, ordered by relevance. Narrow to specific files or to one company, or leave both unset to search the whole account.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to search for. Between 3 and 500 characters"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to these file IDs. Cannot be combined with Company ID"},"companyId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Restrict the search to one company\'s files. Cannot be combined with file IDs"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of files to return, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_list_entries":{"id":"affinity_search_list_entries","name":"Affinity Search List Entries","description":"Search the rows of one list by filters, sorts, and a free-text term. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID to search"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_notes":{"id":"affinity_search_notes","name":"Affinity Search Notes","description":"Search notes by keyword, ordered by relevance. Narrow to specific notes or to one company, or leave both unset to search the whole account.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to search for. Between 3 and 500 characters"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to these note IDs. Cannot be combined with Company ID"},"companyId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Restrict the search to one company\'s notes. Cannot be combined with note IDs"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of notes to return, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_persons":{"id":"affinity_search_persons","name":"Affinity Search Persons","description":"Search persons by filters, sorts, and a free-text term. Requires the \\"Export All People directory\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_semantic_search":{"id":"affinity_semantic_search","name":"Affinity Semantic Search","description":"Find companies from a description in plain language — industry, technology, stage, or business model. Currently searches companies only.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to look for, in plain language, e.g. \\"climate tech companies in our pipeline\\". Up to 500 characters"},"listIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to companies on these lists, e.g. [1, 2]"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of companies to return, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_entity_field_value":{"id":"affinity_update_entity_field_value","name":"Affinity Update Entity Field Value","description":"Write one non-list field value on a company or person. The value type must match how the field is defined.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to write the field on: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to write"},"value":{"type":"json","required":true,"visibility":"user-or-llm","description":"The new value as {type, data}, where type matches the field\'s value type. Examples: {\\"type\\":\\"text\\",\\"data\\":\\"Series B\\"}, {\\"type\\":\\"number\\",\\"data\\":42}, {\\"type\\":\\"dropdown\\",\\"data\\":{\\"dropdownOptionId\\":7}}, {\\"type\\":\\"person\\",\\"data\\":{\\"id\\":123}}, {\\"type\\":\\"person-multi\\",\\"data\\":[{\\"id\\":123}]}. Pass data as null to clear the field"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_list_entry_field":{"id":"affinity_update_list_entry_field","name":"Affinity Update List Entry Field","description":"Write one field value on a list row. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to write"},"value":{"type":"json","required":true,"visibility":"user-or-llm","description":"The new value as {type, data}, where type matches the field\'s value type. Examples: {\\"type\\":\\"text\\",\\"data\\":\\"Series B\\"}, {\\"type\\":\\"number\\",\\"data\\":42}, {\\"type\\":\\"dropdown\\",\\"data\\":{\\"dropdownOptionId\\":7}}, {\\"type\\":\\"person\\",\\"data\\":{\\"id\\":123}}, {\\"type\\":\\"person-multi\\",\\"data\\":[{\\"id\\":123}]}. Pass data as null to clear the field"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_list_field_dropdown_option":{"id":"affinity_update_list_field_dropdown_option","name":"Affinity Update List Field Dropdown Option","description":"Change a dropdown option on a list field. Every field is optional — supply only what should change, and only fields the option\'s kind actually has.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID to update"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Replacement option label. Supply at least one field to change"},"rank":{"type":"number","required":false,"visibility":"user-or-llm","description":"Sort order. Required on a ranked-dropdown or status-dropdown option"},"color":{"type":"string","required":false,"visibility":"user-or-llm","description":"Option color: white, gray, blue, green, purple, orange, or red. Required on a ranked-dropdown or status-dropdown option"},"statusCategory":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pipeline meaning of the option: open, won, lost, or on-hold. Status-dropdown options only"},"winRate":{"type":"number","required":false,"visibility":"user-or-llm","description":"Expected win rate of the status. Status-dropdown options only"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_note":{"id":"affinity_update_note","name":"Affinity Update Note","description":"Rewrite a note\'s body or replace which records it is attached to. Each list of IDs replaces that association wholesale, an empty list clears it, and omitting one leaves it untouched. A note\'s type cannot be changed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID to update"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"Replacement note body as HTML"},"companyIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached companies, e.g. [1, 2]. Send [] to detach every company; omit to leave them unchanged"},"personIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached persons, e.g. [1, 2]. Send [] to detach every person; omit to leave them unchanged"},"opportunityIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached opportunities, e.g. [1, 2]. Send [] to detach every opportunity; omit to leave them unchanged"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_create_draft":{"id":"agentmail_create_draft","name":"Create Draft","description":"Create a new email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to create the draft in"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Draft subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text draft body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML draft body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"},"inReplyTo":{"type":"string","required":false,"visibility":"user-or-llm","description":"ID of message being replied to"},"sendAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to schedule sending"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_create_inbox":{"id":"agentmail_create_inbox","name":"Create Inbox","description":"Create a new email inbox with AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"username":{"type":"string","required":false,"visibility":"user-or-llm","description":"Username for the inbox email address"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Domain for the inbox email address"},"displayName":{"type":"string","required":false,"visibility":"user-or-llm","description":"Display name for the inbox"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_draft":{"id":"agentmail_delete_draft","name":"Delete Draft","description":"Delete an email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to delete"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_inbox":{"id":"agentmail_delete_inbox","name":"Delete Inbox","description":"Delete an email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to delete"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_thread":{"id":"agentmail_delete_thread","name":"Delete Thread","description":"Delete an email thread in AgentMail (moves to trash, or permanently deletes if already in trash)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to delete"},"permanent":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Force permanent deletion instead of moving to trash"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_forward_message":{"id":"agentmail_forward_message","name":"Forward Message","description":"Forward an email message to new recipients in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to forward"},"to":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Override subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Additional plain text to prepend"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"Additional HTML to prepend"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_draft":{"id":"agentmail_get_draft","name":"Get Draft","description":"Get details of a specific email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox the draft belongs to"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_inbox":{"id":"agentmail_get_inbox","name":"Get Inbox","description":"Get details of a specific email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_message":{"id":"agentmail_get_message","name":"Get Message","description":"Get details of a specific email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_thread":{"id":"agentmail_get_thread","name":"Get Thread","description":"Get details of a specific email thread including messages in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_drafts":{"id":"agentmail_list_drafts","name":"List Drafts","description":"List email drafts in an inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list drafts from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of drafts to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_inboxes":{"id":"agentmail_list_inboxes","name":"List Inboxes","description":"List all email inboxes in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of inboxes to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_messages":{"id":"agentmail_list_messages","name":"List Messages","description":"List messages in an inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list messages from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of messages to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_threads":{"id":"agentmail_list_threads","name":"List Threads","description":"List email threads in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list threads from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of threads to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"},"labels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to filter threads by"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter threads before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter threads after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_reply_message":{"id":"agentmail_reply_message","name":"Reply to Message","description":"Reply to an existing email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to reply from"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to reply to"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text reply body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML reply body"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Override recipient email addresses (comma-separated)"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC email addresses (comma-separated)"},"replyAll":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Reply to all recipients of the original message"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_send_draft":{"id":"agentmail_send_draft","name":"Send Draft","description":"Send an existing email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to send"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_send_message":{"id":"agentmail_send_message","name":"Send Message","description":"Send an email message from an AgentMail inbox","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to send from"},"to":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient email address (comma-separated for multiple)"},"subject":{"type":"string","required":true,"visibility":"user-or-llm","description":"Email subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text email body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML email body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_draft":{"id":"agentmail_update_draft","name":"Update Draft","description":"Update an existing email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to update"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Draft subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text draft body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML draft body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"},"sendAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to schedule sending"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_inbox":{"id":"agentmail_update_inbox","name":"Update Inbox","description":"Update the display name of an email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to update"},"displayName":{"type":"string","required":true,"visibility":"user-or-llm","description":"New display name for the inbox"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_message":{"id":"agentmail_update_message","name":"Update Message","description":"Add or remove labels on an email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to update"},"addLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to add to the message"},"removeLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to remove from the message"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_thread":{"id":"agentmail_update_thread","name":"Update Thread Labels","description":"Add or remove labels on an email thread in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to update"},"addLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to add to the thread"},"removeLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to remove from the thread"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_call":{"id":"agentphone_create_call","name":"Create Outbound Call","description":"Initiate an outbound voice call from an AgentPhone agent","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"agentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Agent that will handle the call"},"toNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Phone number to call in E.164 format (e.g. +14155551234)"},"fromNumberId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Phone number ID to use as caller ID. Must belong to the agent. If omitted, the agent\'s first assigned number is used."},"initialGreeting":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optional greeting spoken when the recipient answers"},"voice":{"type":"string","required":false,"visibility":"user-or-llm","description":"Voice ID override for this call (defaults to the agent\'s configured voice)"},"systemPrompt":{"type":"string","required":false,"visibility":"user-or-llm","description":"When provided, uses a built-in LLM for the conversation instead of forwarding to your webhook"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_contact":{"id":"agentphone_create_contact","name":"Create Contact","description":"Create a new contact in AgentPhone","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"phoneNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Phone number in E.164 format (e.g. +14155551234)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact\'s full name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Contact\'s email address"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Freeform notes stored on the contact"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_number":{"id":"agentphone_create_number","name":"Create Phone Number","description":"Provision a new SMS- and voice-enabled phone number","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Two-letter country code (e.g. US, CA). Defaults to US."},"areaCode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Preferred area code (US/CA only, e.g. \\"415\\"). Best-effort — may be ignored if unavailable."},"agentId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optionally attach the number to an agent immediately"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_delete_contact":{"id":"agentphone_delete_contact","name":"Delete Contact","description":"Delete a contact by ID","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_call":{"id":"agentphone_get_call","name":"Get Call","description":"Fetch a call and its full transcript","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"callId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the call to retrieve"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_call_transcript":{"id":"agentphone_get_call_transcript","name":"Get Call Transcript","description":"Get the full ordered transcript for a call","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"callId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the call to retrieve the transcript for"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_contact":{"id":"agentphone_get_contact","name":"Get Contact","description":"Fetch a single contact by ID","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_conversation":{"id":"agentphone_get_conversation","name":"Get Conversation","description":"Get a conversation along with its recent messages","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"messageLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of recent messages to include (default 50, max 100)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_conversation_messages":{"id":"agentphone_get_conversation_messages","name":"Get Conversation Messages","description":"Get paginated messages for a conversation","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of messages to return (default 50, max 200)"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_number_messages":{"id":"agentphone_get_number_messages","name":"Get Phone Number Messages","description":"Fetch messages received on a specific phone number","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"numberId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the phone number"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of messages to return (default 50, max 200)"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage":{"id":"agentphone_get_usage","name":"Get Usage","description":"Retrieve current usage statistics for the AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage_daily":{"id":"agentphone_get_usage_daily","name":"Get Daily Usage","description":"Get a daily breakdown of usage (messages, calls, webhooks) for the last N days","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"days":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of days to return (1-365, default 30)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage_monthly":{"id":"agentphone_get_usage_monthly","name":"Get Monthly Usage","description":"Get monthly usage aggregation (messages, calls, webhooks) for the last N months","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"months":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of months to return (1-24, default 6)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_calls":{"id":"agentphone_list_calls","name":"List Calls","description":"List voice calls for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"},"status":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by status (completed, in-progress, failed)"},"direction":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by direction (inbound, outbound)"},"type":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by call type (pstn, web)"},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search by phone number (matches fromNumber or toNumber)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_contacts":{"id":"agentphone_list_contacts","name":"List Contacts","description":"List contacts for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by name or phone number (case-insensitive contains)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 50, max 200)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_conversations":{"id":"agentphone_list_conversations","name":"List Conversations","description":"List conversations (message threads) for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_numbers":{"id":"agentphone_list_numbers","name":"List Phone Numbers","description":"List all phone numbers provisioned for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_react_to_message":{"id":"agentphone_react_to_message","name":"React to Message","description":"Send an iMessage tapback reaction to a message (iMessage only)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to react to"},"reaction":{"type":"string","required":true,"visibility":"user-or-llm","description":"Reaction type: love, like, dislike, laugh, emphasize, or question"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_release_number":{"id":"agentphone_release_number","name":"Release Phone Number","description":"Release (delete) a phone number. This action is irreversible.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"numberId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the phone number to release"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_send_message":{"id":"agentphone_send_message","name":"Send Message","description":"Send an outbound SMS or iMessage from an AgentPhone agent","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"agentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Agent sending the message"},"toNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient phone number in E.164 format (e.g. +14155551234)"},"body":{"type":"string","required":true,"visibility":"user-or-llm","description":"Message text to send"},"mediaUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optional URL of an image, video, or file to attach"},"numberId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Phone number ID to send from. If omitted, the agent\'s first assigned number is used."}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_update_contact":{"id":"agentphone_update_contact","name":"Update Contact","description":"Update a contact\'s fields","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"},"phoneNumber":{"type":"string","required":false,"visibility":"user-or-llm","description":"New phone number in E.164 format"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New contact name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"New email address"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"New freeform notes"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_update_conversation":{"id":"agentphone_update_conversation","name":"Update Conversation","description":"Update conversation metadata (stored state). Pass null to clear existing metadata.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"metadata":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom key-value metadata to store on the conversation. Pass null to clear existing metadata."}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agiloft_async_status":{"id":"agiloft_async_status","name":"Agiloft Async Status","description":"Check whether an asynchronous Agiloft call, such as a run action button, has completed.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table the asynchronous call was made against"},"callbackId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Callback ID returned by the asynchronous call, e.g. from Run Action Button"}},"hostedApiKey":"none"},"agiloft_attach_file":{"id":"agiloft_attach_file","name":"Agiloft Attach File","description":"Attach a file to a field in an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to attach the file to"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"file":{"type":"file","required":true,"visibility":"user-or-llm","description":"File to attach"},"fileName":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name to assign to the file (defaults to original file name)"},"overwrite":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Replace the contents of the field instead of adding another file to it"}},"hostedApiKey":"none"},"agiloft_attachment_info":{"id":"agiloft_attachment_info","name":"Agiloft Attachment Info","description":"Get information about file attachments on a record field.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to check attachments on"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field to inspect"}},"hostedApiKey":"none"},"agiloft_create_record":{"id":"agiloft_create_record","name":"Agiloft Create Record","description":"Create a new record in an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record field values as a JSON object (e.g., {\\"first_name\\": \\"John\\", \\"status\\": \\"Active\\"})"}},"hostedApiKey":"none"},"agiloft_delete_record":{"id":"agiloft_delete_record","name":"Agiloft Delete Record","description":"Delete a record from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to delete"},"substituteIds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated IDs of records that adopt the dependants of the deleted record. Read only when the delete rule is REPLACE_WITH_ANOTHER."},"deleteRule":{"type":"string","required":false,"visibility":"user-or-llm","description":"How to treat records that depend on this one: ERROR_IF_DEPENDANTS (default — fails rather than cascading), APPLY_DELETE_WHERE_POSSIBLE, DELETE_WHERE_POSSIBLE_OTHERWISE_UNLINK, APPLY_UNLINK, UNLINK_WHERE_POSSIBLE_OTHERWISE_DELETE, or REPLACE_WITH_ANOTHER"}},"hostedApiKey":"none"},"agiloft_get_choice_line_id":{"id":"agiloft_get_choice_line_id","name":"Agiloft Get Choice Line ID","description":"Resolve the internal numeric ID of a choice-list value, for use in EWSelect WHERE clauses against choice fields.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"case\\", \\"contracts\\")"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Choice field name (e.g., \\"priority\\", \\"status\\")"},"value":{"type":"string","required":true,"visibility":"user-or-llm","description":"Choice display value to resolve (e.g., \\"High\\", \\"Active\\")"}},"hostedApiKey":"none"},"agiloft_list_tables":{"id":"agiloft_list_tables","name":"Agiloft List Tables","description":"List the tables and fields in an Agiloft knowledge base, to discover the logical names other operations need.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":false,"visibility":"user-or-llm","description":"Logical name of a single table to describe (e.g., \\"contacts\\"). Leave empty to list every table in the knowledge base."},"includeLinkedInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the source table and column behind each linked field"},"skipColumnsInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Return table names only, omitting field details, for a much smaller response"}},"hostedApiKey":"none"},"agiloft_lock_record":{"id":"agiloft_lock_record","name":"Agiloft Lock Record","description":"Lock, unlock, or check the lock status of an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to lock, unlock, or check"},"lockAction":{"type":"string","required":true,"visibility":"user-or-llm","description":"Action to perform: \\"lock\\", \\"unlock\\", or \\"check\\""},"force":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Unlock only: release a lock held by another user."}},"hostedApiKey":"none"},"agiloft_nlp_search":{"id":"agiloft_nlp_search","name":"Agiloft Natural Language Search","description":"Search Agiloft records by describing what you want in plain language, such as \\"active NDAs submitted last month\\".","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"nlpQuery":{"type":"string","required":true,"visibility":"user-or-llm","description":"The request in plain language, e.g. \\"Show me open, high-priority contracts\\". Structured field filters are not accepted — use Search Records for those."},"fields":{"type":"string","required":true,"visibility":"user-or-llm","description":"Comma-separated field names to return, e.g. \\"id, contract_title1, company_name\\""},"page":{"type":"string","required":false,"visibility":"user-or-llm","description":"Page number, starting from 0"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Records per page"}},"hostedApiKey":"none"},"agiloft_read_record":{"id":"agiloft_read_record","name":"Agiloft Read Record","description":"Read a record by ID from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to read"},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of field names to include in the response"}},"hostedApiKey":"none"},"agiloft_remove_attachment":{"id":"agiloft_remove_attachment","name":"Agiloft Remove Attachment","description":"Remove an attached file from a field in an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record containing the attachment"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"position":{"type":"string","required":true,"visibility":"user-or-llm","description":"Position index of the file to remove (starting from 0)"}},"hostedApiKey":"none"},"agiloft_retrieve_attachment":{"id":"agiloft_retrieve_attachment","name":"Agiloft Retrieve Attachment","description":"Download an attached file from an Agiloft record field.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record containing the attachment"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"position":{"type":"string","required":true,"visibility":"user-or-llm","description":"Position index of the file in the field (starting from 0)"}},"hostedApiKey":"none"},"agiloft_run_action_button":{"id":"agiloft_run_action_button","name":"Agiloft Run Action Button","description":"Run an action button on an Agiloft record, such as an approval or send-for-signature step.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"case\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to run the action button on"},"actionButtonField":{"type":"string","required":true,"visibility":"user-or-llm","description":"Logical name of the field holding the action button (e.g., \\"ab_field\\")"}},"hostedApiKey":"none"},"agiloft_saved_search":{"id":"agiloft_saved_search","name":"Agiloft Saved Search","description":"List the saved searches defined for an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Logical table name to list saved searches for (e.g., \\"contract\\")"}},"hostedApiKey":"none"},"agiloft_search_records":{"id":"agiloft_search_records","name":"Agiloft Search Records","description":"Search for records in an Agiloft table using a query.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name to search in (e.g., \\"contracts\\", \\"contacts.employees\\")"},"query":{"type":"string","required":false,"visibility":"user-or-llm","description":"Ad hoc EWSearch query. Combine conditions with && (and) or || (or) and quote every value — e.g. \\"summary~=\'test\'&&priority=\'High\'\\". Required unless a saved search is given."},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Label of a saved search defined on the table (e.g., \\"C: Status is Closed\\"). Can be combined with a query to narrow it further."},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of field names to include in the results"},"page":{"type":"string","required":false,"visibility":"user-or-llm","description":"Page number for paginated results (starting from 0)"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of records to return per page. Agiloft treats 0 as \\"all records\\", so leave it unset or use a positive value to keep result sizes bounded."}},"hostedApiKey":"none"},"agiloft_select_records":{"id":"agiloft_select_records","name":"Agiloft Select Records","description":"Select record IDs matching a SQL WHERE clause from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"where":{"type":"string","required":true,"visibility":"user-or-llm","description":"SQL WHERE clause using database column names (e.g., \\"summary like \'%new%\'\\" or \\"assigned_person=\'John Doe\'\\"). EWSelect has no page size and returns every matching ID, so append a database limit such as \\"limit 0,200\\" to bound the result."}},"hostedApiKey":"none"},"agiloft_update_record":{"id":"agiloft_update_record","name":"Agiloft Update Record","description":"Update an existing record in an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to update"},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Updated field values as a JSON object (e.g., {\\"status\\": \\"Active\\", \\"priority\\": \\"High\\"})"}},"hostedApiKey":"none"},"agiloft_upsert_record":{"id":"agiloft_upsert_record","name":"Agiloft Upsert Record","description":"Create an Agiloft record, or update it when a record already matches the given fields.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"match":{"type":"string","required":true,"visibility":"user-or-llm","description":"Field used to find an existing record (e.g., \\"ext_id\\"). Pick something that identifies a record uniquely — if more than one record matches, Agiloft writes nothing and returns a conflict."},"async":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Queue the write instead of waiting for it. Returns a callback ID instead of a record ID; pass that to Async Status to poll the result."},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Field values as a JSON object. On create these populate the new record; on update only the supplied fields change."}},"hostedApiKey":"none"},"ahrefs_anchors":{"id":"ahrefs_anchors","name":"Ahrefs Anchors","description":"Get the anchor text distribution for a target domain or URL\'s backlinks, showing how many links and referring domains use each anchor text.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live), \\"all_time\\" (default, includes lost backlinks), or \\"since:YYYY-MM-DD\\" (backlinks found since a date)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_backlinks":{"id":"ahrefs_backlinks","name":"Ahrefs Backlinks","description":"Get a list of backlinks pointing to a target domain or URL. Returns details about each backlink including source URL, anchor text, and domain rating.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live backlinks), \\"all_time\\" (default, includes lost backlinks), or \\"since:YYYY-MM-DD\\" (backlinks found since a date)."},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_backlinks_stats":{"id":"ahrefs_backlinks_stats","name":"Ahrefs Backlinks Stats","description":"Get backlink and referring domain totals for a target domain or URL, both currently live and across all time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_batch_analysis":{"id":"ahrefs_batch_analysis","name":"Ahrefs Batch Analysis","description":"Get bulk SEO metrics (Domain Rating, backlinks, referring domains, organic traffic, and more) for multiple domains or URLs in a single request. Useful for comparing many competitors at once.","version":"1.0.0","params":{"targets":{"type":"string","required":true,"visibility":"user-or-llm","description":"Comma-separated list of domains or URLs to analyze. Example: \\"example.com,competitor.com\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode applied to every target: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"protocol":{"type":"string","required":false,"visibility":"user-or-llm","description":"Protocol applied to every target: \\"both\\" (default), \\"http\\", or \\"https\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_broken_backlinks":{"id":"ahrefs_broken_backlinks","name":"Ahrefs Broken Backlinks","description":"Get a list of broken backlinks pointing to a target domain or URL. Useful for identifying link reclamation opportunities.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_domain_rating":{"id":"ahrefs_domain_rating","name":"Ahrefs Domain Rating","description":"Get the Domain Rating (DR) and Ahrefs Rank for a target domain. Domain Rating shows the strength of a website\'s backlink profile on a scale from 0 to 100.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain to analyze (e.g., example.com)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date for historical data in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_domain_rating_history":{"id":"ahrefs_domain_rating_history","name":"Ahrefs Domain Rating History","description":"Get the historical Domain Rating (DR) trend for a target domain or URL over a date range, grouped daily, weekly, or monthly.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_keyword_overview":{"id":"ahrefs_keyword_overview","name":"Ahrefs Keyword Overview","description":"Get detailed metrics for a keyword including search volume, keyword difficulty, CPC, clicks, and traffic potential.","version":"1.0.0","params":{"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The keyword to analyze"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for keyword data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_keywords_history":{"id":"ahrefs_keywords_history","name":"Ahrefs Keywords History","description":"Get the historical organic keyword ranking distribution for a target domain or URL over a date range: how many keywords rank in each position bucket at each point in time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_metrics":{"id":"ahrefs_metrics","name":"Ahrefs Metrics","description":"Get a one-call organic and paid search overview for a target domain or URL: organic traffic, organic keywords, paid traffic, paid keywords, and estimated traffic cost.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_metrics_history":{"id":"ahrefs_metrics_history","name":"Ahrefs Metrics History","description":"Get the historical organic and paid traffic trend for a target domain or URL over a date range: organic traffic/cost and paid traffic/cost at each point in time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_organic_competitors":{"id":"ahrefs_organic_competitors","name":"Ahrefs Organic Competitors","description":"Get domains that compete with a target domain or URL for the same organic keywords, ranked by keyword overlap.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_organic_keywords":{"id":"ahrefs_organic_keywords","name":"Ahrefs Organic Keywords","description":"Get organic keywords that a target domain or URL ranks for in Google search results. Returns keyword details including search volume, ranking position, and estimated traffic.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_paid_pages":{"id":"ahrefs_paid_pages","name":"Ahrefs Paid Pages","description":"Get a target domain\'s pages that receive paid search traffic, sorted by estimated paid traffic. Returns page URLs with their paid traffic, keyword counts, and estimated spend.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_competitors_overview":{"id":"ahrefs_rank_tracker_competitors_overview","name":"Ahrefs Rank Tracker Competitors Overview","description":"Get competitor rankings for the keywords tracked in an Ahrefs Rank Tracker project: each tracked keyword\'s volume and difficulty alongside every competitor\'s position, traffic, and traffic value. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report rankings for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"dateCompared":{"type":"string","required":false,"visibility":"user-only","description":"Comparison date in YYYY-MM-DD format, to compute position/traffic deltas"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_competitors_stats":{"id":"ahrefs_rank_tracker_competitors_stats","name":"Ahrefs Rank Tracker Competitors Stats","description":"Get aggregate competitor stats for an Ahrefs Rank Tracker project: each competitor\'s traffic, traffic value, average position, and share of voice across all tracked keywords. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report metrics for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_overview":{"id":"ahrefs_rank_tracker_overview","name":"Ahrefs Rank Tracker Overview","description":"Get ranking overview metrics for the keywords tracked in an Ahrefs Rank Tracker project: position, search volume, keyword difficulty, and estimated traffic. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report rankings for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"dateCompared":{"type":"string","required":false,"visibility":"user-only","description":"Comparison date in YYYY-MM-DD format, to compute position/traffic deltas"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_serp_overview":{"id":"ahrefs_rank_tracker_serp_overview","name":"Ahrefs Rank Tracker SERP Overview","description":"Get the full SERP (search engine results page) for a keyword tracked in an Ahrefs Rank Tracker project, including every ranking URL with its position, title, and authority metrics. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The tracked keyword to retrieve SERP data for"},"country":{"type":"string","required":true,"visibility":"user-or-llm","description":"Country code for the tracked keyword. Example: \\"us\\", \\"gb\\", \\"de\\""},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"topPositions":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of top organic positions to return (defaults to all available)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Timestamp to return the last available SERP Overview at, in YYYY-MM-DDThh:mm:ss format"},"locationId":{"type":"number","required":false,"visibility":"user-or-llm","description":"Location ID of the tracked keyword, if tracked at a specific location"},"languageCode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Language code of the tracked keyword"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_refdomains_history":{"id":"ahrefs_refdomains_history","name":"Ahrefs Referring Domains History","description":"Get the historical referring domains trend for a target domain or URL over a date range, grouped daily, weekly, or monthly.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_referring_domains":{"id":"ahrefs_referring_domains","name":"Ahrefs Referring Domains","description":"Get a list of domains that link to a target domain or URL. Returns unique referring domains with their domain rating, backlink counts, and discovery dates.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live), \\"all_time\\" (default, includes lost domains), or \\"since:YYYY-MM-DD\\" (domains found since a date)."},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_related_terms":{"id":"ahrefs_related_terms","name":"Ahrefs Related Terms","description":"Get keyword ideas related to a seed keyword: terms the same top-ranking pages also rank for (\\"also rank for\\") or also discuss (\\"also talk about\\"), with volume, difficulty, and CPC.","version":"1.0.0","params":{"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The seed keyword to find related terms for"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for keyword data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"terms":{"type":"string","required":false,"visibility":"user-or-llm","description":"Type of related keywords to return: \\"also_rank_for\\", \\"also_talk_about\\", or \\"all\\" (default: \\"all\\")"},"viewFor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Whether to derive related terms from the top 10 or top 100 ranking pages (default: \\"top_10\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_site_audit_page_explorer":{"id":"ahrefs_site_audit_page_explorer","name":"Ahrefs Site Audit Page Explorer","description":"Get crawled pages from an Ahrefs Site Audit project with health and SEO metrics: HTTP status, title, link counts, backlinks, indexability, and traffic. Optionally filter to pages affected by a specific issue.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Site Audit project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Crawl date in YYYY-MM-DDThh:mm:ss format (defaults to the most recent crawl)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip, for pagination"},"issueId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Only return pages affected by this issue ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_top_pages":{"id":"ahrefs_top_pages","name":"Ahrefs Top Pages","description":"Get the top pages of a target domain sorted by organic traffic. Returns page URLs with their traffic, keyword counts, and estimated traffic value.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"airtable_create_records":{"id":"airtable_create_records","name":"Airtable Create Records","description":"Write new records to an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to create, each with a `fields` object"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_delete_records":{"id":"airtable_delete_records","name":"Airtable Delete Records","description":"Delete one or more records from an Airtable table by ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordIds":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of record IDs to delete (each starts with \\"rec\\", e.g., [\\"recXXXXXXXXXXXXXX\\"]). Pass a single-element array to delete one record."}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_get_base_schema":{"id":"airtable_get_base_schema","name":"Airtable Get Base Schema","description":"Get the schema of all tables, fields, and views in an Airtable base","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_get_record":{"id":"airtable_get_record","name":"Airtable Get Record","description":"Retrieve a single record from an Airtable table by its ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record ID to retrieve (starts with \\"rec\\", e.g., \\"recXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_bases":{"id":"airtable_list_bases","name":"Airtable List Bases","description":"List all bases the authenticated user has access to","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"offset":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination offset for retrieving additional bases"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_records":{"id":"airtable_list_records","name":"Airtable List Records","description":"Read records from an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"maxRecords":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of records to return (default: all records)"},"filterFormula":{"type":"string","required":false,"visibility":"user-or-llm","description":"Formula to filter records (e.g., \\"({Field Name} = \'Value\')\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_tables":{"id":"airtable_list_tables","name":"Airtable List Tables","description":"List all tables and their schema in an Airtable base","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_update_multiple_records":{"id":"airtable_update_multiple_records","name":"Airtable Update Multiple Records","description":"Update multiple existing records in an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to update, each with an `id` and a `fields` object"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_update_record":{"id":"airtable_update_record","name":"Airtable Update Record","description":"Update an existing record in an Airtable table by ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record ID to update (starts with \\"rec\\", e.g., \\"recXXXXXXXXXXXXXX\\")"},"fields":{"type":"json","required":true,"visibility":"user-or-llm","description":"An object containing the field names and their new values"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_upsert_records":{"id":"airtable_upsert_records","name":"Airtable Upsert Records","description":"Update existing records or create new ones in an Airtable table, matching on the specified merge fields","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to upsert, each with a `fields` object"},"fieldsToMergeOn":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of field names used to match existing records (max 3). A record is updated when all merge fields match, otherwise it is created. Example: [\\"Name\\"]"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"algolia_add_record":{"id":"algolia_add_record","name":"Algolia Add Record","description":"Add or replace a record in an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":false,"visibility":"user-or-llm","description":"Object ID for the record (auto-generated if not provided)"},"record":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object representing the record to add"}},"hostedApiKey":"none"},"algolia_batch_operations":{"id":"algolia_batch_operations","name":"Algolia Batch Operations","description":"Perform batch add, update, partial update, or delete operations on records in an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"requests":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of batch operations. Each item has \\"action\\" (addObject, updateObject, partialUpdateObject, partialUpdateObjectNoCreate, deleteObject, delete, clear) and \\"body\\" (the record data; must include objectID for update/delete; use an empty object {} for the index-level delete/clear actions)"}},"hostedApiKey":"none"},"algolia_browse_records":{"id":"algolia_browse_records","name":"Algolia Browse Records","description":"Browse and iterate over all records in an Algolia index using cursor pagination","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key (must have browse ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to browse"},"query":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search query to filter browsed records"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter string to narrow down results"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of hits per page (default: 1000, max: 1000)"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous browse response for pagination"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search"}},"hostedApiKey":"none"},"algolia_clear_records":{"id":"algolia_clear_records","name":"Algolia Clear Records","description":"Clear all records from an Algolia index while keeping settings, synonyms, and rules","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to clear"}},"hostedApiKey":"none"},"algolia_copy_move_index":{"id":"algolia_copy_move_index","name":"Algolia Copy/Move Index","description":"Copy or move an Algolia index to a new destination","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the source index"},"operation":{"type":"string","required":true,"visibility":"user-or-llm","description":"Operation to perform: \\"copy\\" or \\"move\\""},"destination":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the destination index"},"scope":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of scopes to copy (only for \\"copy\\" operation): [\\"settings\\", \\"synonyms\\", \\"rules\\"]. Omit to copy everything including records."}},"hostedApiKey":"none"},"algolia_delete_by_filter":{"id":"algolia_delete_by_filter","name":"Algolia Delete By Filter","description":"Delete all records matching a filter from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter expression to match records for deletion (e.g., \\"category:outdated\\")"},"facetFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of facet filters (e.g., [\\"brand:Acme\\"])"},"numericFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of numeric filters (e.g., [\\"price > 100\\"])"},"tagFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of tag filters using the _tags attribute (e.g., [\\"published\\"])"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search filter (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search filter"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search filter"}},"hostedApiKey":"none"},"algolia_delete_index":{"id":"algolia_delete_index","name":"Algolia Delete Index","description":"Delete an entire Algolia index and all its records","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to delete"}},"hostedApiKey":"none"},"algolia_delete_record":{"id":"algolia_delete_record","name":"Algolia Delete Record","description":"Delete a record by objectID from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to delete"}},"hostedApiKey":"none"},"algolia_get_record":{"id":"algolia_get_record","name":"Algolia Get Record","description":"Get a record by objectID from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to retrieve"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"}},"hostedApiKey":"none"},"algolia_get_records":{"id":"algolia_get_records","name":"Algolia Get Records","description":"Retrieve multiple records by objectID from one or more Algolia indices","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Default index name for all requests"},"requests":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of objects specifying records to retrieve. Each must have \\"objectID\\" and optionally \\"indexName\\" and \\"attributesToRetrieve\\"."}},"hostedApiKey":"none"},"algolia_get_settings":{"id":"algolia_get_settings","name":"Algolia Get Settings","description":"Retrieve the settings of an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"}},"hostedApiKey":"none"},"algolia_get_task_status":{"id":"algolia_get_task_status","name":"Algolia Get Task Status","description":"Check whether an Algolia indexing task has finished publishing","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index the task ran against"},"taskID":{"type":"number","required":true,"visibility":"user-or-llm","description":"The taskID returned by a previous write operation"}},"hostedApiKey":"none"},"algolia_list_indices":{"id":"algolia_list_indices","name":"Algolia List Indices","description":"List all indices in an Algolia application","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for paginating indices (default: not paginated)"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of indices per page (default: 100)"}},"hostedApiKey":"none"},"algolia_partial_update_record":{"id":"algolia_partial_update_record","name":"Algolia Partial Update Record","description":"Partially update a record in an Algolia index without replacing it entirely","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to update"},"attributes":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object with attributes to update. Supports built-in operations like {\\"stock\\": {\\"_operation\\": \\"Decrement\\", \\"value\\": 1}}"},"createIfNotExists":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to create the record if it does not exist (default: true)"}},"hostedApiKey":"none"},"algolia_search":{"id":"algolia_search","name":"Algolia Search","description":"Search an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to search"},"query":{"type":"string","required":true,"visibility":"user-or-llm","description":"Search query text"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of hits per page (default: 20)"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number to retrieve (default: 0)"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter string (e.g., \\"category:electronics AND price < 100\\")"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"},"facets":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of facet attribute names to retrieve counts for (use \\"*\\" for all)"},"getRankingInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to include detailed ranking information in each hit"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search"}},"hostedApiKey":"none"},"algolia_update_settings":{"id":"algolia_update_settings","name":"Algolia Update Settings","description":"Update the settings of an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have editSettings ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"settings":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object with settings to update (e.g., {\\"searchableAttributes\\": [\\"name\\", \\"description\\"], \\"customRanking\\": [\\"desc(popularity)\\"]})"},"forwardToReplicas":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to apply changes to replica indices (default: false)"}},"hostedApiKey":"none"},"amplitude_event_segmentation":{"id":"amplitude_event_segmentation","name":"Amplitude Event Segmentation","description":"Query event analytics data with segmentation. Get event counts, uniques, averages, and more.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"eventType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Event type name to analyze"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric type: uniques, totals, pct_dau, average, histogram, sums, value_avg, or formula (default: uniques)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by (prefix custom user properties with \\"gp:\\")"},"groupBy2":{"type":"string","required":false,"visibility":"user-or-llm","description":"Second property name to group by (prefix custom user properties with \\"gp:\\")"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of group-by values (max 1000)"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON array of filter objects applied to the event, e.g. [{\\"subprop_type\\":\\"event\\",\\"subprop_key\\":\\"city\\",\\"subprop_op\\":\\"is\\",\\"subprop_value\\":[\\"San Francisco\\"]}]"},"formula":{"type":"string","required":false,"visibility":"user-or-llm","description":"Required when metric is \\"formula\\", e.g. \\"UNIQUES(A)/UNIQUES(B)\\""},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_funnels":{"id":"amplitude_funnels","name":"Amplitude Funnels","description":"Analyze conversion rates and drop-off between a sequence of events.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"events":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON array of event objects, one per funnel step in order, e.g. [{\\"event_type\\":\\"signup\\"},{\\"event_type\\":\\"purchase\\"}]"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Funnel ordering: \\"ordered\\", \\"unordered\\", or \\"sequential\\" (default: ordered)"},"userType":{"type":"string","required":false,"visibility":"user-or-llm","description":"User type: \\"new\\" or \\"active\\" (default: active)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: -300000 (real-time), -3600000 (hourly), 1 (daily), 7 (weekly), or 30 (monthly)"},"conversionWindowSeconds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Conversion window in seconds (default: 2592000, i.e. 30 days)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property to group by (limit: one; prefix custom properties with \\"gp:\\")"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of group-by values (default: 100, max: 1000)"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_get_active_users":{"id":"amplitude_get_active_users","name":"Amplitude Get Active Users","description":"Get active or new user counts over a date range from the Dashboard REST API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric type: \\"active\\" or \\"new\\" (default: active)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_get_revenue":{"id":"amplitude_get_revenue","name":"Amplitude Get Revenue","description":"Get revenue LTV data including ARPU, ARPPU, total revenue, and paying user counts.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric: 0 (ARPU), 1 (ARPPU), 2 (Total Revenue), 3 (Paying Users)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by (limit: one)"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_group_identify":{"id":"amplitude_group_identify","name":"Amplitude Group Identify","description":"Set group-level properties in Amplitude. Supports $set, $setOnce, $add, $append, $unset operations.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"groupType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Group classification (e.g., \\"company\\", \\"org_id\\")"},"groupValue":{"type":"string","required":true,"visibility":"user-or-llm","description":"Specific group identifier (e.g., \\"Acme Corp\\")"},"groupProperties":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON object of group properties. Use operations like $set, $setOnce, $add, $append, $unset."},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_identify_user":{"id":"amplitude_identify_user","name":"Amplitude Identify User","description":"Set user properties in Amplitude using the Identify API. Supports $set, $setOnce, $add, $append, $unset operations.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"User ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"userProperties":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON object of user properties. Use operations like $set, $setOnce, $add, $append, $unset."},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_list_events":{"id":"amplitude_list_events","name":"Amplitude List Events","description":"List all event types in the Amplitude project with their weekly totals and unique counts.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_realtime_active_users":{"id":"amplitude_realtime_active_users","name":"Amplitude Real-time Active Users","description":"Get real-time active user counts at 5-minute granularity for the last 2 days.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_retention":{"id":"amplitude_retention","name":"Amplitude Retention","description":"Measure how many users return to perform an action after a starting action.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"startEvent":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON starting event object, e.g. {\\"event_type\\":\\"_new\\"} or {\\"event_type\\":\\"_active\\"}"},"returnEvent":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON returning event object, e.g. {\\"event_type\\":\\"_all\\"} or {\\"event_type\\":\\"_active\\"}"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"retentionMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Retention type: \\"bracket\\", \\"rolling\\", or \\"n-day\\" (default: n-day)"},"retentionBrackets":{"type":"string","required":false,"visibility":"user-or-llm","description":"Required when Retention Mode is \\"bracket\\". Day ranges, e.g. [[0,4]]"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property to group by (limit: one; prefix custom properties with \\"gp:\\")"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_send_event":{"id":"amplitude_send_event","name":"Amplitude Send Event","description":"Track an event in Amplitude using the HTTP V2 API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"User ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"eventType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the event (e.g., \\"page_view\\", \\"purchase\\")"},"eventProperties":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON object of custom event properties"},"userProperties":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON object of user properties to set (supports $set, $setOnce, $add, $append, $unset)"},"time":{"type":"string","required":false,"visibility":"user-or-llm","description":"Event timestamp in milliseconds since epoch"},"sessionId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Session start time in milliseconds since epoch"},"insertId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Unique ID for deduplication (within 7-day window)"},"appVersion":{"type":"string","required":false,"visibility":"user-or-llm","description":"Application version string"},"platform":{"type":"string","required":false,"visibility":"user-or-llm","description":"Platform (e.g., \\"Web\\", \\"iOS\\", \\"Android\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Two-letter country code"},"language":{"type":"string","required":false,"visibility":"user-or-llm","description":"Language code (e.g., \\"en\\")"},"ip":{"type":"string","required":false,"visibility":"user-or-llm","description":"IP address for geo-location"},"price":{"type":"string","required":false,"visibility":"user-or-llm","description":"Price of the item purchased"},"quantity":{"type":"string","required":false,"visibility":"user-or-llm","description":"Quantity of items purchased"},"revenue":{"type":"string","required":false,"visibility":"user-or-llm","description":"Revenue amount"},"productId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Product identifier"},"revenueType":{"type":"string","required":false,"visibility":"user-or-llm","description":"Revenue type (e.g., \\"purchase\\", \\"refund\\")"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_user_activity":{"id":"amplitude_user_activity","name":"Amplitude User Activity","description":"Get the event stream for a specific user by their Amplitude ID.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"amplitudeId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Amplitude internal user ID"},"offset":{"type":"string","required":false,"visibility":"user-or-llm","description":"Offset for pagination (default 0)"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of events to return (default 1000, max 1000)"},"direction":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort direction: \\"latest\\" or \\"earliest\\" (default: latest)"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"amplitude_user_profile":{"id":"amplitude_user_profile","name":"Amplitude User Profile","description":"Get a user profile including properties, cohort memberships, and computed properties. Not available for EU data-residency projects.","version":"1.0.0","params":{"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"External user ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"getAmpProps":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include Amplitude user properties (true/false, default: false)"},"getCohortIds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include cohort IDs the user belongs to (true/false, default: false)"},"getComputations":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include computed user properties (true/false, default: false)"}},"hostedApiKey":"none"},"amplitude_user_search":{"id":"amplitude_user_search","name":"Amplitude User Search","description":"Search for a user by User ID, Device ID, or Amplitude ID using the Dashboard REST API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"user":{"type":"string","required":true,"visibility":"user-or-llm","description":"User ID, Device ID, or Amplitude ID to search for"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"hostedApiKey":"none"},"apify_get_dataset_items":{"id":"apify_get_dataset_items","name":"APIFY Get Dataset Items","description":"Retrieve items stored in an APIFY dataset","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"datasetId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Dataset ID to read items from. Example: \\"9RnD3Pql2vGZkc5H5\\""},"itemLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Max items to return (1-250000). Default: all items. Example: 500"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to skip at the start. Default: 0"},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of fields to include. Example: \\"title,url,price\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_get_run":{"id":"apify_get_run","name":"APIFY Get Run","description":"Get the status and details of an APIFY actor run","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"runId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor run ID to fetch. Example: \\"HG7ML7M8z78YcAPEB\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_actor_async":{"id":"apify_run_actor_async","name":"APIFY Run Actor (Async)","description":"Start an APIFY Actor run and poll it within the same workflow step until completion or the polling deadline","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"actorId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor ID or username/actor-name. Examples: \\"apify/web-scraper\\", \\"janedoe/my-actor\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor input as JSON string. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ], \\"maxPages\\": 10 }"},"waitForFinish":{"type":"number","required":false,"visibility":"user-or-llm","description":"Initial wait time in seconds (0-60) before polling starts. Example: 30"},"itemLimit":{"type":"number","required":false,"default":100,"visibility":"user-or-llm","description":"Max dataset items to fetch (1-250000). Default: 100. Example: 500"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the actor run (128-32768). Example: 1024 for 1GB, 2048 for 2GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the actor run. Example: 300 for 5 minutes, 3600 for 1 hour"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\", \\"build-tag-name\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_actor_sync":{"id":"apify_run_actor_sync","name":"APIFY Run Actor (Sync)","description":"Run an APIFY actor synchronously and get results (max 5 minutes)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"actorId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor ID or username/actor-name. Examples: \\"apify/web-scraper\\", \\"janedoe/my-actor\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor input as JSON string. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ], \\"maxPages\\": 10 }"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the actor run (128-32768). Example: 1024 for 1GB, 2048 for 2GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the actor run. Example: 300 for 5 minutes, 3600 for 1 hour"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\", \\"build-tag-name\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_task":{"id":"apify_run_task","name":"APIFY Run Task","description":"Run a saved APIFY actor task synchronously and get dataset items (max 5 minutes)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task ID or username/task-name. Examples: \\"janedoe/my-task\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON string that overrides the task\'s saved input. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ] }"},"itemLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Max dataset items to return (1-250000). Example: 500"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the run (128-32768). Example: 1024 for 1GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the run. Example: 300 for 5 minutes"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_bulk_create":{"id":"apollo_account_bulk_create","name":"Apollo Bulk Create Accounts","description":"Create up to 100 accounts at once in your Apollo database. Set run_dedupe=true to deduplicate by domain, organization_id, and name. Master key required.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"accounts":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of accounts to create (max 100). Each account should include a name, and may optionally include domain, phone, phone_status_cd, raw_address, owner_id, linkedin_url, facebook_url, twitter_url, salesforce_id, and hubspot_id."},"append_label_names":{"type":"array","required":false,"visibility":"user-only","description":"Array of label names to add to ALL accounts in this request"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, performs aggressive deduplication by domain, organization_id, and name (defaults to false)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_bulk_update":{"id":"apollo_account_bulk_update","name":"Apollo Bulk Update Accounts","description":"Update up to 1000 existing accounts at once in your Apollo database (higher limit than contacts!). Each account must include an id field. Master key required.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"account_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of account IDs to update with the same values (max 1000). Use with name/owner_id for uniform updates. Use either this OR account_attributes."},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this name to all accounts"},"owner_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this owner to all accounts"},"account_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this account stage to all accounts"},"account_attributes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of account objects with individual updates (each must include id). Example: [{\\"id\\": \\"acc1\\", \\"name\\": \\"Acme\\", \\"owner_id\\": \\"u1\\", \\"account_stage_id\\": \\"s1\\", \\"typed_custom_fields\\": {\\"field_id\\": \\"value\\"}}]"},"async":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, processes the update asynchronously. Only supported when using account_ids; returns 422 if used with account_attributes."}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_create":{"id":"apollo_account_create","name":"Apollo Create Account","description":"Create a new account (company) in your Apollo database","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Company name (e.g., \\"Acme Corporation\\")"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain without www. prefix (e.g., \\"acme.com\\")"},"phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number for the account"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo user ID of the account owner"},"account_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo ID for the account stage to assign this account to"},"raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate location (e.g., \\"San Francisco, CA, USA\\")"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_search":{"id":"apollo_account_search","name":"Apollo Search Accounts","description":"Search your team\'s accounts in Apollo. Display limit: 50,000 records (100 records per page, 500 pages max). Use filters to narrow results. Master key required.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"q_organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter accounts by organization name (partial-match search)"},"account_stage_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by account stage IDs"},"account_label_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by account label IDs"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"account_last_activity_date\\", \\"account_created_at\\", or \\"account_updated_at\\""},"sort_ascending":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Sort ascending when true. Defaults to descending."},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_update":{"id":"apollo_account_update","name":"Apollo Update Account","description":"Update an existing account in your Apollo database","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"account_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the account to update (e.g., \\"acc_abc123\\")"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company name (e.g., \\"Acme Corporation\\")"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain (e.g., \\"acme.com\\")"},"phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company phone number"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo user ID of the account owner"},"account_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo ID for the account stage to assign this account to"},"raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate location (e.g., \\"San Francisco, CA, USA\\")"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_contact_bulk_create":{"id":"apollo_contact_bulk_create","name":"Apollo Bulk Create Contacts","description":"Create up to 100 contacts at once in your Apollo database. Supports deduplication to prevent creating duplicate contacts. Master key required.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"contacts":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of contacts to create (max 100). Each contact may include first_name, last_name, email, title, organization_name, account_id, owner_id, contact_stage_id, linkedin_url, phone (single string) or phone_numbers (array of {raw_number, position}), contact_emails, typed_custom_fields, and CRM IDs (salesforce_contact_id, hubspot_id, team_id) for cross-system matching"},"append_label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Label names to add to all contacts in this request (e.g., [\\"Hot Lead\\"])"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-only","description":"Enable deduplication to prevent creating duplicate contacts. When true, existing contacts are returned without modification"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_contact_bulk_update":{"id":"apollo_contact_bulk_update","name":"Apollo Bulk Update Contacts","description":"Update up to 100 existing contacts at once in your Apollo database. Each contact must include an id field. Master key required.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"contact_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of contact IDs to update. Must be paired with an object-form contact_attributes specifying the fields to apply uniformly to all listed contacts."},"contact_attributes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Required. Either an array of per-contact updates (each with id) — used standalone — or a single object of attributes to apply to all contact_ids. Supported fields: owner_id, email, organization_name, title, first_name, last_name, account_id, present_raw_address, linkedin_url, typed_custom_fields"},"async":{"type":"boolean","required":false,"visibility":"user-only","description":"Force asynchronous processing. Automatically enabled for >100 contacts"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_contact_create":{"id":"apollo_contact_create","name":"Apollo Create Contact","description":"Create a new contact in your Apollo database","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"first_name":{"type":"string","required":true,"visibility":"user-or-llm","description":"First name of the contact"},"last_name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Last name of the contact"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address of the contact"},"title":{"type":"string","required":false,"visibility":"user-or-llm","description":"Job title (e.g., \\"VP of Sales\\", \\"Software Engineer\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo account ID to associate with (e.g., \\"acc_abc123\\")"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the contact owner (accepted by Apollo but not officially documented for POST /contacts)"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the contact\'s employer (e.g., \\"Apollo\\")"},"website_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate website URL (e.g., \\"https://www.apollo.io/\\")"},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Lists/labels to add the contact to (e.g., [\\"Prospects\\"])"},"contact_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the contact stage"},"present_raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal location for the contact (e.g., \\"Atlanta, United States\\")"},"direct_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number"},"corporate_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Work/office phone number"},"mobile_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Mobile phone number"},"home_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Home phone number"},"other_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Alternative phone number"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom field values keyed by custom field ID"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, Apollo deduplicates against existing contacts"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_contact_search":{"id":"apollo_contact_search","name":"Apollo Search Contacts","description":"Search your team\'s contacts in Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"q_keywords":{"type":"string","required":false,"visibility":"user-or-llm","description":"Keywords to search for"},"contact_stage_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by contact stage IDs"},"contact_label_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by Apollo label IDs (lists)"},"sort_by_field":{"type":"string","required":false,"visibility":"user-only","description":"Sort field: contact_last_activity_date, contact_email_last_opened_at, contact_email_last_clicked_at, contact_created_at, or contact_updated_at"},"sort_ascending":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, sort ascending. Must be used together with sort_by_field"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_contact_update":{"id":"apollo_contact_update","name":"Apollo Update Contact","description":"Update an existing contact in your Apollo database","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"contact_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the contact to update (e.g., \\"con_abc123\\")"},"first_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"First name of the contact"},"last_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Last name of the contact"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address"},"title":{"type":"string","required":false,"visibility":"user-or-llm","description":"Job title (e.g., \\"VP of Sales\\", \\"Software Engineer\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo account ID (e.g., \\"acc_abc123\\")"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the contact owner (accepted by Apollo but not officially documented for PATCH /contacts/{id})"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the contact\'s employer (e.g., \\"Apollo\\")"},"website_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate website URL (e.g., \\"https://www.apollo.io/\\")"},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Lists/labels to add the contact to (e.g., [\\"Prospects\\"])"},"contact_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the contact stage"},"present_raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal location for the contact (e.g., \\"Atlanta, United States\\")"},"direct_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number"},"corporate_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Work/office phone number"},"mobile_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Mobile phone number"},"home_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Home phone number"},"other_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Alternative phone number"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom field values keyed by custom field ID"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_email_accounts":{"id":"apollo_email_accounts","name":"Apollo Get Email Accounts","description":"Get list of team\'s linked email accounts in Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_opportunity_create":{"id":"apollo_opportunity_create","name":"Apollo Create Opportunity","description":"Create a new deal for an account in your Apollo database (master key required)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the opportunity/deal (e.g., \\"Enterprise License - Q1\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"ID of the account this opportunity belongs to (e.g., \\"acc_abc123\\")"},"amount":{"type":"string","required":false,"visibility":"user-or-llm","description":"Monetary value as a plain number string with no commas or currency symbols"},"opportunity_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the opportunity stage"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the opportunity owner"},"closed_date":{"type":"string","required":false,"visibility":"user-or-llm","description":"Expected close date in YYYY-MM-DD format"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_opportunity_get":{"id":"apollo_opportunity_get","name":"Apollo Get Opportunity","description":"Retrieve complete details of a specific deal/opportunity by ID","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"opportunity_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the opportunity to retrieve (e.g., \\"opp_abc123\\")"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_opportunity_search":{"id":"apollo_opportunity_search","name":"Apollo Search Opportunities","description":"Search and list all deals/opportunities in your team\'s Apollo account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"amount\\", \\"is_closed\\", or \\"is_won\\""},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_opportunity_update":{"id":"apollo_opportunity_update","name":"Apollo Update Opportunity","description":"Update an existing deal/opportunity in your Apollo database","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"opportunity_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the opportunity to update (e.g., \\"opp_abc123\\")"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the opportunity/deal (e.g., \\"Enterprise License - Q1\\")"},"amount":{"type":"string","required":false,"visibility":"user-or-llm","description":"Monetary value as a plain number string with no commas or currency symbols"},"opportunity_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the opportunity stage"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the opportunity owner"},"closed_date":{"type":"string","required":false,"visibility":"user-or-llm","description":"Expected close date in YYYY-MM-DD format"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_organization_bulk_enrich":{"id":"apollo_organization_bulk_enrich","name":"Apollo Bulk Organization Enrichment","description":"Enrich data for up to 10 organizations at once using Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"domains":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of company domains to enrich (max 10, no www. or @, e.g., [\\"apollo.io\\", \\"stripe.com\\"])"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_organization_enrich":{"id":"apollo_organization_enrich","name":"Apollo Organization Enrichment","description":"Enrich data for a single organization using Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"domain":{"type":"string","required":true,"visibility":"user-or-llm","description":"Company domain (e.g., \\"apollo.io\\", \\"acme.com\\")"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_organization_search":{"id":"apollo_organization_search","name":"Apollo Organization Search","description":"Search Apollo\'s database for companies using filters","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"organization_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Company HQ locations (cities, US states, or countries)"},"organization_not_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Exclude companies whose HQ is in these locations"},"organization_num_employees_ranges":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employee count ranges as \\"min,max\\" strings (e.g., [\\"1,10\\", \\"250,500\\", \\"10000,20000\\"])"},"q_organization_keyword_tags":{"type":"array","required":false,"visibility":"user-or-llm","description":"Industry or keyword tags"},"q_organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Organization name to search for (e.g., \\"Acme\\", \\"TechCorp\\")"},"organization_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Apollo organization IDs to include (e.g., [\\"5e66b6381e05b4008c8331b8\\"])"},"q_organization_domains_list":{"type":"array","required":false,"visibility":"user-or-llm","description":"Domain names to filter by (no www. or @, up to 1,000)"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_people_bulk_enrich":{"id":"apollo_people_bulk_enrich","name":"Apollo Bulk People Enrichment","description":"Enrich data for up to 10 people at once using Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"people":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of people to enrich (max 10)"},"reveal_personal_emails":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal personal email addresses (uses credits)"},"reveal_phone_number":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal phone numbers (uses credits, requires webhook_url)"},"webhook_url":{"type":"string","required":false,"visibility":"user-only","description":"Webhook URL for async phone number delivery (required when reveal_phone_number is true)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_people_enrich":{"id":"apollo_people_enrich","name":"Apollo People Enrichment","description":"Enrich data for a single person using Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"first_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"First name of the person"},"last_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Last name of the person"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Full name of the person (alternative to first_name/last_name)"},"id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the person"},"hashed_email":{"type":"string","required":false,"visibility":"user-or-llm","description":"MD5 or SHA-256 hashed email"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address of the person"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company name where the person works"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain (e.g., \\"apollo.io\\", \\"acme.com\\")"},"linkedin_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"LinkedIn profile URL"},"reveal_personal_emails":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal personal email addresses (uses credits)"},"reveal_phone_number":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal phone numbers (uses credits, requires webhook_url)"},"webhook_url":{"type":"string","required":false,"visibility":"user-only","description":"Webhook URL for async phone number delivery (required when reveal_phone_number is true)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_people_search":{"id":"apollo_people_search","name":"Apollo People Search","description":"Search Apollo\'s database for people using demographic filters","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"person_titles":{"type":"array","required":false,"visibility":"user-or-llm","description":"Job titles to search for (e.g., [\\"CEO\\", \\"VP of Sales\\"])"},"include_similar_titles":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to return people with job titles similar to person_titles"},"person_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Locations to search in (e.g., [\\"San Francisco, CA\\", \\"New York, NY\\"])"},"person_seniorities":{"type":"array","required":false,"visibility":"user-or-llm","description":"Seniority levels (one of: owner, founder, c_suite, partner, vp, head, director, manager, senior, entry, intern)"},"organization_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Apollo organization IDs to filter by (e.g., [\\"5e66b6381e05b4008c8331b8\\"])"},"organization_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Company names to search within (legacy filter)"},"organization_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Headquarters locations of the people\'s current employer (e.g., [\'texas\', \'tokyo\', \'spain\'])"},"q_organization_domains_list":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employer domain names (e.g., [\\"apollo.io\\", \\"microsoft.com\\"]) — up to 1,000, no www. or @"},"organization_num_employees_ranges":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employee count ranges for the person\'s current employer. Each entry is \\"min,max\\" (e.g., [\\"1,10\\", \\"250,500\\", \\"10000,20000\\"])"},"contact_email_status":{"type":"array","required":false,"visibility":"user-or-llm","description":"Email statuses to filter by: \\"verified\\", \\"unverified\\", \\"likely to engage\\", \\"unavailable\\""},"q_keywords":{"type":"string","required":false,"visibility":"user-or-llm","description":"Keywords to search for"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination, default 1 (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, default 25, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_sequence_add_contacts":{"id":"apollo_sequence_add_contacts","name":"Apollo Add Contacts to Sequence","description":"Add contacts to an Apollo sequence","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"sequence_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the sequence to add contacts to (e.g., \\"seq_abc123\\")"},"contact_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of contact IDs to add to the sequence (e.g., [\\"con_abc123\\", \\"con_def456\\"]). Either contact_ids or label_names must be provided."},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of label names to identify contacts to add to the sequence. Either contact_ids or label_names must be provided."},"send_email_from_email_account_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the email account to send from. Use the Get Email Accounts operation to look this up."},"send_email_from_email_address":{"type":"string","required":false,"visibility":"user-only","description":"Specific email address to send from within the email account."},"sequence_no_email":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if they have no email address"},"sequence_unverified_email":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts with unverified email addresses"},"sequence_job_change":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts who recently changed jobs"},"sequence_active_in_other_campaigns":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts active in other campaigns"},"sequence_finished_in_other_campaigns":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts who finished other campaigns"},"sequence_same_company_in_same_campaign":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if others from the same company are in the sequence"},"contacts_without_ownership_permission":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts without ownership permission"},"add_if_in_queue":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if they are in the queue"},"contact_verification_skipped":{"type":"boolean","required":false,"visibility":"user-only","description":"Skip contact verification when adding"},"user_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the user performing the action"},"status":{"type":"string","required":false,"visibility":"user-only","description":"Initial status for added contacts: \\"active\\" or \\"paused\\""},"auto_unpause_at":{"type":"string","required":false,"visibility":"user-only","description":"ISO 8601 datetime to automatically unpause contacts"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_sequence_search":{"id":"apollo_sequence_search","name":"Apollo Search Sequences","description":"Search for sequences/campaigns in your team\'s Apollo account (master key required)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"q_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search sequences by name (e.g., \\"Outbound Q1\\", \\"Follow-up\\")"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_task_create":{"id":"apollo_task_create","name":"Apollo Create Task","description":"Create one or more tasks in Apollo (one task per contact_id, master key required)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"user_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the Apollo user the task is assigned to"},"contact_ids":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of contact IDs. One task is created per contact."},"priority":{"type":"string","required":false,"visibility":"user-or-llm","description":"Task priority: \\"high\\", \\"medium\\", or \\"low\\" (defaults to \\"medium\\")"},"due_at":{"type":"string","required":true,"visibility":"user-or-llm","description":"Due date/time in ISO 8601 format (e.g., \\"2024-12-31T23:59:59Z\\")"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task type: \\"call\\", \\"outreach_manual_email\\", \\"linkedin_step_connect\\", \\"linkedin_step_message\\", \\"linkedin_step_view_profile\\", \\"linkedin_step_interact_post\\", or \\"action_item\\""},"status":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task status: \\"scheduled\\", \\"completed\\", or \\"skipped\\""},"note":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-form note providing context for the task"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_task_search":{"id":"apollo_task_search","name":"Apollo Search Tasks","description":"Search for tasks in Apollo","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"task_due_at\\" or \\"task_priority\\""},"open_factor_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Filter by status. Common values: [\\"task_types\\"] for open tasks, [\\"task_completed_at\\"] for completed tasks."},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"appconfig_create_application":{"id":"appconfig_create_application","name":"AppConfig Create Application","description":"Create an application in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the application to create"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the application"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_configuration_profile":{"id":"appconfig_create_configuration_profile","name":"AppConfig Create Configuration Profile","description":"Create a configuration profile in an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to create the configuration profile in"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the configuration profile"},"locationUri":{"type":"string","required":true,"visibility":"user-or-llm","description":"Where the configuration is stored. Use \\"hosted\\" for AppConfig-hosted configurations, or an SSM/S3 URI"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the configuration profile"},"retrievalRoleArn":{"type":"string","required":false,"visibility":"user-or-llm","description":"ARN of an IAM role to retrieve the configuration (required for non-hosted URIs)"},"type":{"type":"string","required":false,"visibility":"user-or-llm","description":"Profile type: AWS.Freeform (default) or AWS.AppConfig.FeatureFlags"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_environment":{"id":"appconfig_create_environment","name":"AppConfig Create Environment","description":"Create an environment for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to create the environment in"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the environment to create"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the environment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_hosted_configuration_version":{"id":"appconfig_create_hosted_configuration_version","name":"AppConfig Create Hosted Configuration Version","description":"Create a new hosted configuration version for an AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to add the version to"},"content":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration content (e.g., a JSON or YAML document)"},"contentType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Content type of the configuration (e.g., application/json, text/plain)"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the configuration version"},"latestVersionNumber":{"type":"number","required":false,"visibility":"user-or-llm","description":"The version number of the latest version, used for optimistic concurrency"},"versionLabel":{"type":"string","required":false,"visibility":"user-or-llm","description":"A user-defined label for the configuration version"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_application":{"id":"appconfig_delete_application","name":"AppConfig Delete Application","description":"Delete an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_configuration_profile":{"id":"appconfig_delete_configuration_profile","name":"AppConfig Delete Configuration Profile","description":"Delete an AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_environment":{"id":"appconfig_delete_environment","name":"AppConfig Delete Environment","description":"Delete an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_hosted_configuration_version":{"id":"appconfig_delete_hosted_configuration_version","name":"AppConfig Delete Hosted Configuration Version","description":"Delete a specific hosted configuration version from an AppConfig profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID that owns the version"},"versionNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The version number to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_application":{"id":"appconfig_get_application","name":"AppConfig Get Application","description":"Get details about a single AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_configuration":{"id":"appconfig_get_configuration","name":"AppConfig Get Configuration","description":"Retrieve the latest deployed configuration for an AppConfig application, environment, and profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID or name to retrieve configuration for"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID or name to retrieve configuration for"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID or name to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_configuration_profile":{"id":"appconfig_get_configuration_profile","name":"AppConfig Get Configuration Profile","description":"Get details about a single AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_deployment":{"id":"appconfig_get_deployment","name":"AppConfig Get Deployment","description":"Get details about a specific AWS AppConfig deployment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployment"},"deploymentNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The sequence number of the deployment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_environment":{"id":"appconfig_get_environment","name":"AppConfig Get Environment","description":"Get details about a single AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_hosted_configuration_version":{"id":"appconfig_get_hosted_configuration_version","name":"AppConfig Get Hosted Configuration Version","description":"Retrieve a specific hosted configuration version from an AppConfig profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to read the version from"},"versionNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The version number to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_applications":{"id":"appconfig_list_applications","name":"AppConfig List Applications","description":"List applications in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of applications to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_configuration_profiles":{"id":"appconfig_list_configuration_profiles","name":"AppConfig List Configuration Profiles","description":"List configuration profiles for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profiles"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of configuration profiles to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_deployment_strategies":{"id":"appconfig_list_deployment_strategies","name":"AppConfig List Deployment Strategies","description":"List deployment strategies available in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of deployment strategies to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_deployments":{"id":"appconfig_list_deployments","name":"AppConfig List Deployments","description":"List deployments for an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployments"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployments"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of deployments to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_environments":{"id":"appconfig_list_environments","name":"AppConfig List Environments","description":"List environments for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environments"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of environments to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_hosted_configuration_versions":{"id":"appconfig_list_hosted_configuration_versions","name":"AppConfig List Hosted Configuration Versions","description":"List hosted configuration versions for an AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to list versions for"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of versions to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_start_deployment":{"id":"appconfig_start_deployment","name":"AppConfig Start Deployment","description":"Start deploying a configuration version to an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to deploy in"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to deploy to"},"deploymentStrategyId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The deployment strategy ID to use"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to deploy"},"configurationVersion":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration version to deploy"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the deployment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_stop_deployment":{"id":"appconfig_stop_deployment","name":"AppConfig Stop Deployment","description":"Stop an in-progress AWS AppConfig deployment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployment"},"deploymentNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The sequence number of the deployment to stop"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_application":{"id":"appconfig_update_application","name":"AppConfig Update Application","description":"Update the name or description of an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the application"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the application"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_configuration_profile":{"id":"appconfig_update_configuration_profile","name":"AppConfig Update Configuration Profile","description":"Update the name, description, or retrieval role of an AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the configuration profile"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the configuration profile"},"retrievalRoleArn":{"type":"string","required":false,"visibility":"user-or-llm","description":"New ARN of the IAM role used to retrieve the configuration"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_environment":{"id":"appconfig_update_environment","name":"AppConfig Update Environment","description":"Update the name or description of an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the environment"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the environment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"arxiv_get_author_papers":{"id":"arxiv_get_author_papers","name":"ArXiv Get Author Papers","description":"Search for papers by a specific author on ArXiv.","version":"1.0.0","params":{"authorName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Author name to search for"},"maxResults":{"type":"number","required":false,"visibility":"user-only","description":"Maximum number of results to return (default: 10, max: 2000)"}},"hostedApiKey":"none"},"arxiv_get_paper":{"id":"arxiv_get_paper","name":"ArXiv Get Paper","description":"Get detailed information about a specific ArXiv paper by its ID.","version":"1.0.0","params":{"paperId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ArXiv paper ID (e.g., \\"1706.03762\\")"}},"hostedApiKey":"none"},"arxiv_search":{"id":"arxiv_search","name":"ArXiv Search","description":"Search for academic papers on ArXiv by keywords, authors, titles, or other fields.","version":"1.0.0","params":{"searchQuery":{"type":"string","required":true,"visibility":"user-or-llm","description":"The search query to execute"},"searchField":{"type":"string","required":false,"visibility":"user-only","description":"Field to search in: all, ti (title), au (author), abs (abstract), co (comment), jr (journal), cat (category), rn (report number)"},"maxResults":{"type":"number","required":false,"visibility":"user-only","description":"Maximum number of results to return (default: 10, max: 2000)"},"sortBy":{"type":"string","required":false,"visibility":"user-only","description":"Sort by: relevance, lastUpdatedDate, submittedDate (default: relevance)"},"sortOrder":{"type":"string","required":false,"visibility":"user-only","description":"Sort order: ascending, descending (default: descending)"}},"hostedApiKey":"none"},"asana_add_comment":{"id":"asana_add_comment","name":"Asana Add Comment","description":"Add a comment (story) to an Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana task GID (numeric string)"},"text":{"type":"string","required":true,"visibility":"user-or-llm","description":"The text content of the comment"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_add_followers":{"id":"asana_add_followers","name":"Asana Add Followers","description":"Add one or more followers to an Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana task (numeric string)"},"followers":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of user GIDs to add as followers to the task"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_project":{"id":"asana_create_project","name":"Asana Create Project","description":"Create a new project in an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) where the project will be created"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the project"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the project"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_section":{"id":"asana_create_section","name":"Asana Create Section","description":"Create a new section in an Asana project","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana project (numeric string) to add the section to"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the section"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_subtask":{"id":"asana_create_subtask","name":"Asana Create Subtask","description":"Create a subtask under an existing Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the parent Asana task (numeric string)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the subtask"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the subtask"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"User GID to assign the subtask to"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_task":{"id":"asana_create_task","name":"Asana Create Task","description":"Create a new task in Asana","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) where the task will be created"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the task"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the task"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"User GID to assign the task to"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_delete_task":{"id":"asana_delete_task","name":"Asana Delete Task","description":"Delete an Asana task by its GID (moves it to the trash)","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana task to delete (numeric string)"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_project":{"id":"asana_get_project","name":"Asana Get Project","description":"Retrieve a single Asana project by its GID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana project GID (numeric string) to retrieve"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_projects":{"id":"asana_get_projects","name":"Asana Get Projects","description":"Retrieve all projects from an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to retrieve projects from"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_task":{"id":"asana_get_task","name":"Asana Get Task","description":"Retrieve a single task by GID or get multiple tasks with filters","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":false,"visibility":"user-or-llm","description":"The globally unique identifier (GID) of the task. If not provided, will get multiple tasks."},"workspace":{"type":"string","required":false,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to filter tasks (required when not using taskGid)"},"project":{"type":"string","required":false,"visibility":"user-or-llm","description":"Asana project GID (numeric string) to filter tasks"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of tasks to return (default: 50)"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_list_sections":{"id":"asana_list_sections","name":"Asana List Sections","description":"List all sections in an Asana project","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana project (numeric string) to list sections from"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_list_workspaces":{"id":"asana_list_workspaces","name":"Asana List Workspaces","description":"List all Asana workspaces and organizations the authenticated user belongs to","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_search_tasks":{"id":"asana_search_tasks","name":"Asana Search Tasks","description":"Search for tasks in an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to search tasks in"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Text to search for in task names"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter tasks by assignee user GID"},"projects":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of Asana project GIDs (numeric strings) to filter tasks by"},"completed":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Filter by completion status"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_update_task":{"id":"asana_update_task","name":"Asana Update Task","description":"Update an existing task in Asana","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana task GID (numeric string) of the task to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated name for the task"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated notes or description for the task"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated assignee user GID"},"completed":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Mark task as completed or not completed"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"ashby_add_candidate_tag":{"id":"ashby_add_candidate_tag","name":"Ashby Add Candidate Tag","description":"Adds a tag to a candidate in Ashby and returns the updated candidate.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to add the tag to"},"tagId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the tag to add"}},"hostedApiKey":"none"},"ashby_anonymize_candidate":{"id":"ashby_anonymize_candidate","name":"Ashby Anonymize Candidate","description":"Remove personally identifiable information from an Ashby candidate while retaining the candidate record and its applications. This operation does not delete the candidate. Requires the candidatesWrite permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"UUID of the candidate to anonymize"}},"hostedApiKey":"none"},"ashby_change_application_source":{"id":"ashby_change_application_source","name":"Ashby Change Application Source","description":"Changes the source attributed to an existing application, so programmatically created applications report correctly on the recruiting side. Requires the candidatesWrite permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"UUID of the application whose source should change"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to attribute the application to, as returned by List Sources. Omit only when unsetSource is true."},"unsetSource":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Set true to deliberately clear the application source. Required to unset, so that a missing or empty sourceId cannot wipe attribution by accident."}},"hostedApiKey":"none"},"ashby_change_application_stage":{"id":"ashby_change_application_stage","name":"Ashby Change Application Stage","description":"Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the application to update the stage of"},"interviewStageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the interview stage to move the application to"},"archiveReasonId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Archive reason UUID. Required when moving to an Archived stage, ignored otherwise"},"archiveEmail":{"type":"json","required":false,"visibility":"user-or-llm","description":"Archive email configuration with communicationTemplateId and optional sendAt ISO 8601 timestamp. Pass null or omit to send no archive email."}},"hostedApiKey":"none"},"ashby_create_application":{"id":"ashby_create_application","name":"Ashby Create Application","description":"Creates a new application for a candidate on a job. Optionally specify interview plan, stage, source, and credited user.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to consider for the job"},"jobId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the job to consider the candidate for"},"interviewPlanId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the interview plan to use (defaults to the job default plan)"},"interviewStageId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the interview stage to place the application in, or FirstPreInterviewScreen (defaults to the first Lead stage)"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to set on the application"},"creditedToUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the user the application is credited to"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to set as the application creation date (defaults to now)"},"applicationHistory":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional documented application history entries to create with the application"}},"hostedApiKey":"none"},"ashby_create_candidate":{"id":"ashby_create_candidate","name":"Ashby Create Candidate","description":"Creates a new candidate record in Ashby.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"The candidate full name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary email address for the candidate"},"phoneNumber":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number for the candidate"},"linkedInUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"LinkedIn profile URL"},"githubUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"GitHub profile URL"},"website":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal website URL"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to attribute the candidate to"},"creditedToUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the Ashby user to credit with sourcing this candidate"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"Backdated creation timestamp in ISO 8601 (e.g. 2024-01-01T00:00:00Z). Defaults to now."},"alternateEmailAddresses":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of additional email address strings to add to the candidate, e.g. [\\"a@x.com\\",\\"b@y.com\\"]"},"location":{"type":"json","required":false,"visibility":"user-or-llm","description":"Candidate location object with optional city, region, and country"}},"hostedApiKey":"none"},"ashby_create_note":{"id":"ashby_create_note","name":"Ashby Create Note","description":"Creates a note on a candidate in Ashby. Supports plain text and HTML content (bold, italic, underline, links, lists, code).","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to add the note to"},"note":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note content. If noteType is text/html, supports: , , , ,