From e2faba6cc11a4313d1508d1992386406f45474c5 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Thu, 19 Mar 2026 17:02:57 +0000 Subject: [PATCH 01/13] feat: add stackone-connector-builder plugin with full OAS schema references --- .../.claude-plugin/plugin.json | 11 + .../stackone-connector-builder/README.md | 68 ++ .../references/cli-commands.md | 74 ++ .../references/connector-patterns.md | 255 +++++ .../references/field-mapping-guide.md | 202 ++++ .../references/schemas/accounting.md | 120 +++ .../references/schemas/ats.md | 887 ++++++++++++++++++ .../references/schemas/crm.md | 150 +++ .../references/schemas/documents.md | 135 +++ .../references/schemas/hris.md | 847 +++++++++++++++++ .../references/schemas/iam.md | 165 ++++ .../references/schemas/lms.md | 264 ++++++ .../references/schemas/marketing.md | 236 +++++ .../references/schemas/ticketing.md | 180 ++++ .../references/unified-schemas.md | 31 + .../skills/check-connector/SKILL.md | 140 +++ .../skills/choose-schema/SKILL.md | 173 ++++ .../skills/map-fields/SKILL.md | 118 +++ .../skills/scope-actions/SKILL.md | 114 +++ .../stackone-connector-builder/SKILL.md | 145 +++ .../skills/test-connector/SKILL.md | 130 +++ .../skills/validate-connector/SKILL.md | 114 +++ package-lock.json | 8 +- package.json | 2 +- 24 files changed, 4564 insertions(+), 5 deletions(-) create mode 100644 .claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json create mode 100644 .claude/plugins/stackone-connector-builder/README.md create mode 100644 .claude/plugins/stackone-connector-builder/references/cli-commands.md create mode 100644 .claude/plugins/stackone-connector-builder/references/connector-patterns.md create mode 100644 .claude/plugins/stackone-connector-builder/references/field-mapping-guide.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/accounting.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/ats.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/crm.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/documents.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/hris.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/iam.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/lms.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/marketing.md create mode 100644 .claude/plugins/stackone-connector-builder/references/schemas/ticketing.md create mode 100644 .claude/plugins/stackone-connector-builder/references/unified-schemas.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md diff --git a/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json b/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json new file mode 100644 index 0000000..885657a --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "stackone-connector-builder", + "version": "1.0.0", + "description": "Interactive step-by-step guide for building unified Falcon connectors. Walks external builders through schema selection, connector scaffolding, action scoping, field mapping, validation, and testing.", + "author": { + "name": "StackOne", + "email": "engineering@stackone.com" + }, + "license": "MIT", + "keywords": ["connector", "falcon", "unified", "schema", "stackone", "builder"] +} diff --git a/.claude/plugins/stackone-connector-builder/README.md b/.claude/plugins/stackone-connector-builder/README.md new file mode 100644 index 0000000..680d08f --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/README.md @@ -0,0 +1,68 @@ +# stackone-connector-builder + +Interactive step-by-step wizard for building unified Falcon connectors. Guides external builders through every phase — from choosing an output schema to live API testing. + +## Installation + +```bash +/plugin install stackone-connector-builder@stackone-claude-marketplace +``` + +## Usage + +### Full wizard (recommended) + +```bash +/build-connector +``` + +Walks through all 6 steps in sequence. Progress is saved to `.connector-build-session.json` so you can pause and resume at any time. + +### Individual steps + +Each step can also be run independently. It reads the session file and picks up from the right context: + +| Command | Step | What it does | +|---------|------|-------------| +| `/choose-schema` | 1 | Pick a StackOne built-in schema, point to your own, or define one interactively | +| `/check-connector` | 2 | Check if a base connector exists, pull it or scaffold a new one | +| `/scope-actions` | 3 | Choose which resources and CRUD operations to expose | +| `/map-fields` | 4 | Map provider API fields to your schema, writes YAML partials | +| `/validate-connector` | 5 | Validate the YAML config structure | +| `/test-connector` | 6 | Test live against the provider API and verify field output | + +### Resume a session + +```bash +/build-connector +``` + +If a `.connector-build-session.json` exists, the wizard will offer to resume from where you left off. + +## What it builds + +A set of YAML configuration files in `src/configs//` that expose provider API data through StackOne's unified API. Supports both StackOne built-in schemas (HRIS, ATS, CRM, etc.) and custom schemas you define. + +## Schema options + +| Option | When to use | +|--------|-------------| +| **StackOne built-in** | Your data fits HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, or Marketing | +| **Existing schema file** | You already have a JSON/YAML schema in your project | +| **Define inline** | You want to define fields interactively during the wizard | + +## Requirements + +- `connectors-template` project structure (`src/configs/` directory) +- Node.js — for the StackOne CLI (optional but recommended) +- Provider API credentials in `.env` + +## How sessions work + +Each step reads and writes `.connector-build-session.json` at the project root. This file stores: +- Provider name and schema choice +- CLI availability and connector setup status +- Action scope and known limitations +- Validation and test results + +You can safely delete this file to start fresh. diff --git a/.claude/plugins/stackone-connector-builder/references/cli-commands.md b/.claude/plugins/stackone-connector-builder/references/cli-commands.md new file mode 100644 index 0000000..72ac83b --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/cli-commands.md @@ -0,0 +1,74 @@ +# StackOne CLI Reference + +Used by the `check-connector` and `validate-connector` sub-skills. + +## Installation Check + +```bash +npx @stackone/cli --version +``` + +If this returns a version number, the CLI is available. If it errors, proceed with manual scaffolding. + +## Key Commands + +### Validate a connector config +```bash +npx @stackone/cli validate src/configs//.connector.s1.yaml +``` +Checks YAML structure, required fields, and action definitions. Returns validation errors with line numbers. + +### List available connectors +```bash +npx @stackone/cli list +``` +Returns a list of provider names StackOne has indexed. Use to check if a connector already exists before scaffolding from scratch. + +### Pull an existing connector +```bash +npx @stackone/cli pull +``` +Downloads the connector config to `src/configs//`. Creates the folder and YAML files if they don't exist. Use as a starting point. + +### Test an action +```bash +npx @stackone/cli test +``` +Runs a live API call against the action using credentials in `.env`. Returns the raw provider response followed by the mapped unified response. + +Add `--debug` to see the raw provider response before field mapping is applied: +```bash +npx @stackone/cli test --debug +``` + +### Scaffold a new connector +```bash +npx @stackone/cli scaffold +``` +Creates `src/configs//` with a starter `.connector.s1.yaml`. Does NOT pull from StackOne's index — creates a blank template. + +## Environment Setup + +The CLI reads credentials from `.env` in the project root. Variable names follow `_` in uppercase: + +```bash +# .env examples +BAMBOOHR_API_KEY=your_key_here +SALESFORCE_CLIENT_ID=your_client_id +SALESFORCE_CLIENT_SECRET=your_client_secret +WORKDAY_BASE_URL=https://api.workday.com +``` + +## Common CLI Errors + +| Error | Cause | Fix | +|-------|-------|-----| +| `YAML parse error at line N` | Invalid YAML syntax | Check indentation — use 2 spaces, not tabs | +| `Missing required field: entrypointUrl` | Action missing URL | Add `entrypointUrl` to the action | +| `Unknown actionType: custom2` | Typo in actionType | Valid: `custom`, `list`, `get`, `create`, `update`, `delete` | +| `schemaType required for unified action` | Missing schemaType | Add `schemaType: unified` to the action | +| `fieldConfigs required when schemaType is unified` | Missing field mapping | Add `fieldConfigs` array to the action | +| `Missing step: map_fields` | Unified action missing pipeline step | Add all three steps: `request`, `map_fields`, `typecast` | +| `401 Unauthorized` | Bad credentials | Check `.env` and the `authentication` block in connector YAML | +| `404 Not Found` | Wrong endpoint URL | Verify `entrypointUrl` against provider API docs | +| `Cannot find module '@stackone/cli'` | CLI not installed | Run `npm install -g @stackone/cli` or use `npx` | diff --git a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md new file mode 100644 index 0000000..3af6ce7 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md @@ -0,0 +1,255 @@ +# Connector Patterns Reference + +YAML patterns for the most common connector configurations. +Used by `map-fields` and `validate-connector` sub-skills. + +## File Structure + +``` +src/configs// +├── .connector.s1.yaml # Main connector (auth, base config) +└── ..s1.partial.yaml # Actions for each resource +``` + +## Minimal Connector (API Key / Bearer Token) + +```yaml +name: my-provider +version: "1.0" +authentication: + type: custom + args: + api_key: + value: "${MY_PROVIDER_API_KEY}" + headers: + Authorization: "Bearer ${args.api_key.value}" +baseUrl: "https://api.my-provider.com/v1" + +actions: [] +``` + +## Connector with OAuth2 + +```yaml +name: my-provider +version: "1.0" +authentication: + type: oauth2 + clientId: "${MY_PROVIDER_CLIENT_ID}" + clientSecret: "${MY_PROVIDER_CLIENT_SECRET}" + tokenUrl: "https://auth.my-provider.com/oauth/token" + scopes: + - read:employees + - read:departments +baseUrl: "https://api.my-provider.com/v1" + +actions: [] +``` + +## Connector with Basic Auth + +```yaml +name: my-provider +version: "1.0" +authentication: + type: custom + args: + username: + value: "${MY_PROVIDER_USERNAME}" + password: + value: "${MY_PROVIDER_PASSWORD}" + headers: + Authorization: "Basic ${base64(args.username.value + ':' + args.password.value)}" +baseUrl: "https://api.my-provider.com/v1" + +actions: [] +``` + +## Non-Unified Action (raw provider response) + +Use `actionType: custom` when you want to return the provider's raw response without mapping. + +```yaml +- name: list_employees + actionType: custom + entrypointUrl: "/employees" + entrypointHttpMethod: GET + inputs: + page: + type: number + required: false + page_size: + type: number + required: false + steps: + - type: request + id: fetch_employees +``` + +## Unified Action — list (paginated) + +```yaml +- name: unified_list_employees + actionType: list + schemaType: unified + schema: hris/employees + entrypointUrl: "/employees" + entrypointHttpMethod: GET + cursor: + type: page + requestParam: page + responsePath: $.meta.next_page + fieldConfigs: + - targetFieldKey: id + expression: "$.id" + - targetFieldKey: first_name + expression: "$.firstName" + - targetFieldKey: last_name + expression: "$.lastName" + - targetFieldKey: work_email + expression: "$.workEmail" + - targetFieldKey: employment_status + expression: "$.status" + enumMapper: + active: active + inactive: inactive + terminated: inactive + steps: + - type: request + id: fetch_employees + - type: map_fields + version: 2 + id: map_employee_fields + - type: typecast + version: 2 + id: cast_types +``` + +## Unified Action — get (single record by ID) + +```yaml +- name: unified_get_employee + actionType: get + schemaType: unified + schema: hris/employees + entrypointUrl: "/employees/${inputs.id}" + entrypointHttpMethod: GET + inputs: + id: + type: string + required: true + fieldConfigs: + - targetFieldKey: id + expression: "$.id" + - targetFieldKey: first_name + expression: "$.firstName" + steps: + - type: request + id: fetch_employee + - type: map_fields + version: 2 + id: map_fields + - type: typecast + version: 2 + id: typecast +``` + +## Unified Action — create + +```yaml +- name: unified_create_employee + actionType: create + schemaType: unified + schema: hris/employees + entrypointUrl: "/employees" + entrypointHttpMethod: POST + inputs: + first_name: + type: string + required: true + last_name: + type: string + required: true + work_email: + type: string + required: true + steps: + - type: request + id: create_employee + - type: map_fields + version: 2 + id: map_fields + - type: typecast + version: 2 + id: typecast +``` + +## Partial File Pattern ($ref) + +Keep the main connector clean by referencing partial files for each resource: + +```yaml +# in .connector.s1.yaml +actions: + - $ref: "./.employees.s1.partial.yaml" + - $ref: "./.time_off.s1.partial.yaml" + - $ref: "./.departments.s1.partial.yaml" +``` + +Each partial contains a raw YAML array of actions: + +```yaml +# in .employees.s1.partial.yaml +- name: unified_list_employees + actionType: list + # ... + +- name: unified_get_employee + actionType: get + # ... +``` + +## Pagination Patterns + +### Page number (most common) +```yaml +cursor: + type: page + requestParam: page + responsePath: $.pagination.next_page +``` + +### Cursor / token +```yaml +cursor: + type: cursor + requestParam: cursor + responsePath: $.meta.next_cursor +``` + +### Offset +```yaml +cursor: + type: offset + requestParam: offset + pageSize: 100 +``` + +### Link header (RFC 5988) +```yaml +cursor: + type: link_header +``` + +## Auth Credential Variable Naming + +| Auth Type | Variable Pattern | Example | +|-----------|----------------|---------| +| API Key | `${PROVIDER_API_KEY}` | `${BAMBOOHR_API_KEY}` | +| OAuth Client ID | `${PROVIDER_CLIENT_ID}` | `${SALESFORCE_CLIENT_ID}` | +| OAuth Secret | `${PROVIDER_CLIENT_SECRET}` | `${SALESFORCE_CLIENT_SECRET}` | +| Username | `${PROVIDER_USERNAME}` | `${WORKDAY_USERNAME}` | +| Password | `${PROVIDER_PASSWORD}` | `${WORKDAY_PASSWORD}` | +| Base URL | `${PROVIDER_BASE_URL}` | `${WORKDAY_BASE_URL}` | + +Reference inside the connector using `${args..value}` after defining in the `args` block. diff --git a/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md b/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md new file mode 100644 index 0000000..f5010df --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md @@ -0,0 +1,202 @@ +````markdown +# Field Mapping Guide + +Detailed reference for writing `fieldConfigs` in unified connectors. +Used by the `map-fields` sub-skill. + +## The Golden Rule + +`targetFieldKey` is ALWAYS your schema field name — NEVER the provider's field name. + +```yaml +# CORRECT — targetFieldKey uses your schema name +- targetFieldKey: first_name + expression: "$.firstName" + +# WRONG — targetFieldKey uses the provider's name +- targetFieldKey: firstName + expression: "$.firstName" +``` + +--- + +## Expression Types + +### Direct JSONPath +```yaml +- targetFieldKey: id + expression: "$.id" + +- targetFieldKey: first_name + expression: "$.firstName" +``` + +### Nested path +```yaml +- targetFieldKey: department_name + expression: "$.department.name" + +- targetFieldKey: work_email + expression: "$.contact.emails[0].value" +``` + +### JEXL expression (computed or conditional) + +Wrap JEXL in single-quotes with double-curly-brace syntax: + +```yaml +# Combine two fields into one +- targetFieldKey: display_name + expression: "'{{$.firstName}} {{$.lastName}}'" + +# Fallback — use second value if first is null/undefined +- targetFieldKey: email + expression: "'{{$.workEmail || $.personalEmail}}'" + +# Conditional +- targetFieldKey: is_active + expression: "'{{$.status === \"active\" ? true : false}}'" +``` + +### Array of primitive values +```yaml +- targetFieldKey: tags + expression: "$.tags" + array: true +``` + +--- + +## Enum Mapping + +Translate provider-specific values to your schema's enum values: + +```yaml +- targetFieldKey: employment_status + expression: "$.status" + enumMapper: + # provider_value: schema_value + active: active + inactive: inactive + terminated: inactive + on_leave: leave + pending_hire: pending +``` + +**Important:** Keys are case-sensitive and must match the provider's exact values. + +For complex logic, use `matchExpression` (JEXL): +```yaml +- targetFieldKey: gender + expression: "$.gender" + enumMapper: + matchExpression: "'{{$.gender === \"M\" ? \"male\" : $.gender === \"F\" ? \"female\" : \"other\"}}'" +``` + +--- + +## Nested Object Mapping + +For schema fields that are objects (e.g., `department: { id, name }`): + +```yaml +- targetFieldKey: department + objectMapping: + id: + expression: "$.departmentId" + name: + expression: "$.departmentName" +``` + +If the provider returns the nested object directly: +```yaml +- targetFieldKey: department + expression: "$.department" + objectMapping: + id: + expression: "$.id" + name: + expression: "$.name" +``` + +--- + +## Array of Objects Mapping + +For schema fields that are arrays of objects (e.g., `phone_numbers: [{ type, value }]`): + +```yaml +- targetFieldKey: phone_numbers + array: true + expression: "$.phones" + objectMapping: + type: + expression: "$.phoneType" + value: + expression: "$.phoneNumber" +``` + +--- + +## Common Provider-to-Schema Field Patterns + +| Provider Pattern | Schema Field | Expression | Notes | +|----------------|-------------|-----------|-------| +| `firstName` | `first_name` | `$.firstName` | camelCase → snake_case | +| `first_name` | `first_name` | `$.first_name` | already snake_case | +| `properties.email` | `work_email` | `$.properties.email` | HubSpot-style nested | +| `emails[0]` | `work_email` | `$.emails[0]` | First item of array | +| `contact.phone` | `phone` | `$.contact.phone` | Nested path | +| Unix timestamp | `created_at` | `$.created_at` | typecast handles conversion | +| ISO date string | `hire_date` | `$.hireDate` | typecast handles conversion | +| `department.id` + `department.name` | `department` | objectMapping pattern | | + +--- + +## Required Step Functions for Unified Actions + +All unified actions MUST include all three steps in this exact order: + +```yaml +steps: + - type: request # 1. Fetch data from provider API + id: fetch_data + + - type: map_fields # 2. Apply fieldConfigs transformations + version: 2 + id: map_fields + + - type: typecast # 3. Convert types to match schema (dates, numbers, etc.) + version: 2 + id: typecast +``` + +Non-unified (`actionType: custom`) actions only need the `request` step. + +--- + +## Debugging Field Mapping Issues + +### See the raw provider response +```bash +npx @stackone/cli test --debug +``` +This shows exactly what the provider returns before any mapping. Use it to find the correct JSONPath. + +### Field is null in output +1. Check the JSONPath against the raw response — is the field actually there? +2. Is the field nested? Try `$.nested.field` or `$.items[0].field` +3. Does the provider only return this field sometimes? Handle with JEXL fallback. + +### Enum value passes through unmapped +- Check that the `enumMapper` key matches the provider's exact value (case-sensitive) +- Use `--debug` to see the raw enum value coming from the provider + +### Date field is wrong format +- Ensure `typecast` step is present — it handles date/datetime conversions +- Ensure the schema field type is `date` or `datetime`, not `string` + +### Nested object is empty +- Verify `objectMapping` is at the same level as `targetFieldKey` +- Check the expression points to the right parent object in the raw response +```` diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/accounting.md b/.claude/plugins/stackone-connector-builder/references/schemas/accounting.md new file mode 100644 index 0000000..060661a --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/accounting.md @@ -0,0 +1,120 @@ +# Accounting — Schema Reference + +> Manage accounts, transactions, contacts and financial data. + +> **OAS source:** `https://docs.stackone.com/accounting/api-reference/accounting.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/accounting/companies` | List Companies | +| `GET` | `/unified/accounting/companies/{id}` | Get Company | +| `GET` | `/unified/accounting/companies/{id}/accounts` | List Accounts | +| `GET` | `/unified/accounting/companies/{id}/accounts/{subResourceId}` | Get Account | +| `GET` | `/unified/accounting/companies/{id}/tax_rates` | List Tax Rates | +| `GET` | `/unified/accounting/companies/{id}/tax_rates/{subResourceId}` | Get Tax Rate | +| `POST` | `/unified/accounting/companies/{id}/journals/batch` | Batch Create Journals | +| `GET` | `/unified/accounting/companies/{id}/journals` | List Journals | +| `POST` | `/unified/accounting/companies/{id}/journals` | Create Journal | +| `GET` | `/unified/accounting/companies/{id}/journals/{subResourceId}` | Get Journal | + +--- + +## Models + +### `AccountingAccount` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `company_id` | `string` | ID of the company this account belongs to | +| `code` | `string` | External system's account code/number | +| `name` | `string` | Name of the account | +| `type` | `AccountTypeEnum` | Type of account | +| `active` | `object` | Whether the account is active | + +### `AccountingCompany` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Name of the company | +| `base_currency` | `CurrencyEnum` | Default currency for the company | +| `fiscal_year_start_month` | `number` | Fiscal year start month (1-12) | +| `fiscal_year_start_day` | `number` | Fiscal year start day (1-31) | + +### `AccountingJournal` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `company_id` | `string` | ID of the company this journal belongs to | +| `reference` | `string` | Reference number for the journal | +| `memo` | `string` | Memo or description for the journal | +| `transaction_date` | `string` | Date of the journal transaction | +| `status` | `AccountingJournalStatusEnum` | Status of the journal | +| `lines` | `array` | List of journal lines | +| `created_at` | `string` | Timestamp when the journal was created | +| `updated_at` | `string` | Timestamp when the journal was last updated | +| `posted_at` | `string` | Timestamp when the journal was posted | + +### `AccountingTaxRate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `company_id` | `string` | ID of the company this tax rate belongs to | +| `name` | `string` | Name of the tax rate | +| `code` | `string` | External system's tax code | +| `percentage` | `number` | Tax rate percentage | +| `active` | `object` | Whether the tax rate is active | + +### `CreateJournalLine` +| Field | Type | Description | +|-------|------|-------------| +| `account_id` | `string` | ID of the ledger account this line references | +| `description` | `string` | Description of the journal line | +| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negativ... | +| `tax_rate_id` | `string` | ID of the tax rate applied | +| `tax_amount` | `number` | Tax amount of the line in minor units, e.g. 10010 for 100.10 USD | + +### `JournalLine` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier for the journal line | +| `account_id` | `string` | ID of the ledger account this line references | +| `account_type` | `AccountTypeEnum` | Type of the account | +| `description` | `string` | Description of the journal line | +| `currency_code` | `CurrencyEnum` | Currency code for this line | +| `exchange_rate` | `number` | Exchange rate to company base currency | +| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negativ... | +| `tax_amount` | `number` | Tax amount of the line in minor units, e.g. 10010 for 100.10 USD | +| `tax_rate_percentage` | `number` | Tax rate percentage | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `AccountTypeEnum` | `asset`, `liability`, `equity`, `revenue`, `expense` | +| `AccountingJournalStatusEnum` | `draft`, `posted`, `void` | +| `CurrencyEnum` | `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLP`, `CNY`, `COP`, `CRC`, `CUC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLL`, `SOS`, `SRD`, `SSP`, `STN`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XDR`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/ats.md b/.claude/plugins/stackone-connector-builder/references/schemas/ats.md new file mode 100644 index 0000000..5bc085c --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/ats.md @@ -0,0 +1,887 @@ +# ATS (Applicant Tracking System) — Schema Reference + +> Manage jobs, candidates, applications, interviews, offers and departments. + +> **OAS source:** `https://docs.stackone.com/ats/api-reference/ats.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/ats/applications` | List Applications | +| `POST` | `/unified/ats/applications` | Create Application | +| `GET` | `/unified/ats/applications/{id}` | Get Application | +| `PATCH` | `/unified/ats/applications/{id}` | Update Application | +| `GET` | `/unified/ats/applications/{id}/offers` | List Application Offers | +| `POST` | `/unified/ats/applications/{id}/move` | Move Application | +| `POST` | `/unified/ats/applications/{id}/reject` | Reject Application | +| `GET` | `/unified/ats/applications/{id}/offers/{subResourceId}` | Get Application Offer | +| `GET` | `/unified/ats/applications/{id}/scorecards` | List Application Scorecards | +| `GET` | `/unified/ats/applications/{id}/scorecards/{subResourceId}` | Get Application Scorecard | +| `GET` | `/unified/ats/applications/{id}/changes` | List Application Changes | +| `GET` | `/unified/ats/applications/{id}/notes` | List Application Notes | +| `POST` | `/unified/ats/applications/{id}/notes` | Create Application Note | +| `GET` | `/unified/ats/applications/{id}/notes/{subResourceId}` | Get Application Note | +| `PATCH` | `/unified/ats/applications/{id}/notes/{subResourceId}` | Update Application Note | +| `GET` | `/unified/ats/applications/{id}/scheduled_interviews` | List Applications scheduled interviews | +| `GET` | `/unified/ats/applications/{id}/scheduled_interviews/{subResourceId}` | Get Applications scheduled interview | +| `POST` | `/unified/ats/applications/{id}/documents/upload` | Upload Application Document | +| `GET` | `/unified/ats/applications/{id}/documents/{subResourceId}/download` | Download Application Document | +| `GET` | `/unified/ats/applications/{id}/documents` | List Application Documents | +| `GET` | `/unified/ats/applications/{id}/documents/{subResourceId}` | Get Application Document | +| `GET` | `/unified/ats/candidates` | List Candidates | +| `POST` | `/unified/ats/candidates` | Create Candidate | +| `GET` | `/unified/ats/candidates/{id}` | Get Candidate | +| `PATCH` | `/unified/ats/candidates/{id}` | Update Candidate | +| `GET` | `/unified/ats/candidates/{id}/notes` | List Candidate Notes | +| `POST` | `/unified/ats/candidates/{id}/notes` | Create Candidate Note | +| `GET` | `/unified/ats/candidates/{id}/notes/{subResourceId}` | Get Candidate Note | +| `GET` | `/unified/ats/custom_field_definitions/applications` | List Application Custom Field Definitions | +| `GET` | `/unified/ats/custom_field_definitions/applications/{id}` | Get Application Custom Field Definition | +| `GET` | `/unified/ats/custom_field_definitions/candidates` | List Candidate Custom Field Definitions | +| `GET` | `/unified/ats/custom_field_definitions/candidates/{id}` | Get Candidate Custom Field Definition | +| `GET` | `/unified/ats/custom_field_definitions/jobs` | List Job Custom Field Definitions | +| `GET` | `/unified/ats/custom_field_definitions/jobs/{id}` | Get Job Custom Field Definition | +| `GET` | `/unified/ats/departments` | List Departments | +| `GET` | `/unified/ats/departments/{id}` | Get Department | +| `GET` | `/unified/ats/interview_stages` | List Interview Stages | +| `GET` | `/unified/ats/interview_stages/{id}` | Get Interview Stage | +| `GET` | `/unified/ats/application_stages` | List Application Stages | +| `GET` | `/unified/ats/application_stages/{id}` | Get Application Stage | +| `GET` | `/unified/ats/interviews` | List Interviews | +| `GET` | `/unified/ats/interviews/{id}` | Get Interview | +| `POST` | `/unified/ats/interviews/{id}/notes` | Create Interview Note | +| `PATCH` | `/unified/ats/interviews/{id}/notes/{subResourceId}` | Update Interview Note | +| `GET` | `/unified/ats/jobs` | List Jobs | +| `POST` | `/unified/ats/jobs` | Create Job | +| `GET` | `/unified/ats/jobs/{id}/application_stages` | List Job Application Stages | +| `GET` | `/unified/ats/jobs/{id}` | Get Job | +| `PATCH` | `/unified/ats/jobs/{id}` | Update Job | +| `GET` | `/unified/ats/jobs/{id}/application_stages/{subResourceId}` | Get Job Application Stage | +| `GET` | `/unified/ats/lists` | Get all Lists | +| `GET` | `/unified/ats/lists/{id}` | Get List | +| `GET` | `/unified/ats/locations` | List locations | +| `GET` | `/unified/ats/locations/{id}` | Get Location | +| `GET` | `/unified/ats/rejected_reasons` | List Rejected Reasons | +| `GET` | `/unified/ats/rejected_reasons/{id}` | Get Rejected Reason | +| `GET` | `/unified/ats/users` | List Users | +| `GET` | `/unified/ats/users/{id}` | Get User | +| `GET` | `/unified/ats/job_postings` | List Job Postings | +| `GET` | `/unified/ats/job_postings/{id}` | Get Job Posting | +| `GET` | `/unified/ats/offers` | List Offers | +| `POST` | `/unified/ats/offers` | Create Offer | +| `GET` | `/unified/ats/offers/{id}` | Get Offer | +| `GET` | `/unified/ats/assessments/packages` | List Assessments Packages | +| `GET` | `/unified/ats/assessments/packages/{id}` | Get Assessments Package | +| `POST` | `/unified/ats/assessments/orders` | Order Assessments Request | +| `PATCH` | `/unified/ats/assessments/orders/{id}/result` | Update Assessments Result | +| `GET` | `/unified/ats/background_checks/packages` | List Background Check Packages | +| `POST` | `/unified/ats/background_checks/packages` | Create Background Check Package | +| `GET` | `/unified/ats/background_checks/packages/{id}` | Get Background Check Package | +| `PATCH` | `/unified/ats/background_checks/packages/{id}` | Update Background Check Package | +| `DELETE` | `/unified/ats/background_checks/packages/{id}` | Delete Background Check Package | +| `POST` | `/unified/ats/background_checks/orders` | Order Background Check Request | +| `PATCH` | `/unified/ats/background_checks/orders/{id}/result` | Update Background Check Result | +| `GET` | `/unified/ats/documents/application_categories` | List Application Document Categories | +| `GET` | `/unified/ats/documents/application_categories/{id}` | Get Application Document Category | + +--- + +## Models + +### `ATSLocation` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | | + +### `Answer` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `type` | `AnswerEnum` | Type of the answer | +| `values` | `array` | Values of the answer | + +### `Application` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `candidate_id` | `string` | Unique identifier of the candidate | +| `remote_candidate_id` | `string` | Provider's unique identifier of the candidate | +| `job_id` | `string` | Unique identifier of the job | +| `remote_job_id` | `string` | Provider's unique identifier of the job | +| `interview_stage` | `InterviewStage` | | +| `application_stage` | `ApplicationStage` | | +| `interview_stage_id` | `string` | Unique identifier of the interview stage | +| `remote_interview_stage_id` | `string` | Provider's unique identifier of the interview stage | +| `application_stage_id` | `string` | Unique identifier of the application stage | +| `remote_application_stage_id` | `string` | Unique identifier of the application stage | +| `rejected_reasons` | `array` | | +| `rejected_reason_ids` | `array` | Unique identifiers of the rejection reasons | +| `remote_rejected_reason_ids` | `array` | Provider's unique identifiers of the rejection reasons | +| `rejected_at` | `string` | Date of rejection | +| `location_id` | `string` | Unique identifier of the location | +| `remote_location_id` | `string` | Provider's unique identifier of the location | +| `location_ids` | `array` | Unique identifiers of the locations | +| `remote_location_ids` | `array` | Remote's unique identifiers of the locations | +| `application_status` | `ApplicationStatusEnum` | | +| `questionnaires` | `array` | Questionnaires associated with the application | +| `candidate` | `ApplicationCandidate` | | +| `attachments` | `array` | Use `documents` expand instead | +| `documents` | `array` | The documents attached to this application (eg. resume, cover letter etc.) | +| `result_links` | `array` | | +| `source` | `Source` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `custom_fields` | `array` | The application custom fields | + +### `ApplicationAttachment` +| Field | Type | Description | +|-------|------|-------------| +| `file_name` | `string` | The file name of the attachment. | +| `content` | `string` | The content of the attachment. | +| `url` | `string` | The URL of the attachment. | +| `content_type` | `AttachmentContentType` | | + +### `ApplicationCandidate` +| Field | Type | Description | +|-------|------|-------------| +| `name` | `string` | Candidate name | +| `first_name` | `string` | First name of the candidate | +| `last_name` | `string` | Last name of the candidate | +| `email` | `string` | Email of the candidate | +| `emails` | `array` | List of candidate emails | +| `phone_numbers` | `array` | List of candidate phone numbers including the type of the number when available | +| `social_links` | `array` | List of candidate social links | +| `company` | `string` | Candidate company | +| `title` | `string` | Candidate title | + +### `ApplicationChanges` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `created_at` | `string` | Timestamp when the change was created | +| `effective_at` | `string` | Timestamp when the change became effective | +| `actor` | `ChangesActor` | The actor who made the change | +| `change_type` | `ApplicationChangeTypeEnum` | The type of change that occurred to the application | +| `new_values` | `ApplicationChangesDataModel` | The new values for changed application properties. Only includes fields that commonly change over... | + +### `ApplicationChangesDataModel` +| Field | Type | Description | +|-------|------|-------------| +| `interview_stage_id` | `string` | Unique identifier of the interview stage | +| `rejected_reason_ids` | `array` | Unique identifiers of the rejection reasons | +| `application_status` | `ApplicationStatusEnum` | | + +### `ApplicationStage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | Application Stage name | +| `order` | `number` | Application Stage order | +| `created_at` | `string` | Application Stage created date | +| `updated_at` | `string` | Application Stage updated date | + +### `AssessmentPackage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | + +### `AtsDocumentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the file | +| `category` | `FileCategoryEnumApiModel` | The category of the the document | +| `contents` | `array` | The content of the file. Deprecated, use `url` and `file_format` one level up instead | +| `category_id` | `string` | The categoryId of the documents | +| `created_at` | `string` | The creation date of the file | +| `updated_at` | `string` | The update date of the file | +| `remote_url` | `string` | URL where the file content is located | +| `file_format` | `FileFormatEnum` | The file format of the file | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `type` | `AtsDocumentTypeEnum` | The content type of the document | + +### `AtsJob` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `code` | `string` | Code of the job | +| `title` | `string` | Title of the job | +| `description` | `string` | Description of the job | +| `status` | `string` | Status of the job | +| `job_status` | `AtsJobStatusEnum` | Status of the job | +| `department_ids` | `array` | Department ids of the job | +| `remote_department_ids` | `array` | Provider's department ids of the job | +| `location_ids` | `array` | Location ids of the job | +| `remote_location_ids` | `array` | Provider's location ids of the job | +| `hiring_team` | `array` | Hiring team for the job. | +| `interview_stages` | `array` | Interview stages for the job. | +| `confidential` | `string` | Confidential status of the job | +| `custom_fields` | `array` | The job custom fields | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `AtsJobHiringTeam` +| Field | Type | Description | +|-------|------|-------------| +| `user_id` | `string` | User ID of the hiring team member. | +| `remote_user_id` | `string` | Provider's unique identifier of the user | +| `first_name` | `string` | First name of the hiring team member. | +| `last_name` | `string` | Last name of the hiring team member. | +| `email` | `string` | Email of the hiring team member. | +| `role` | `string` | Role of the hiring team member. | + +### `Attachment` +| Field | Type | Description | +|-------|------|-------------| +| `url` | `string` | The URL of the attachment. | +| `content_type` | `AttachmentContentType` | | + +### `BackgroundCheckPackage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | +| `tests` | `array` | Package tests | + +### `Candidate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | Candidate name | +| `first_name` | `string` | Candidate first name | +| `last_name` | `string` | Candidate last name | +| `email` | `string` | Candidate email | +| `emails` | `array` | List of candidate emails | +| `social_links` | `array` | List of candidate social links | +| `phone` | `string` | Candidate phone number | +| `phone_numbers` | `array` | List of candidate phone numbers including the type of the number when available | +| `company` | `string` | Candidate company | +| `title` | `string` | Candidate title | +| `application_ids` | `array` | List of candidate application IDs | +| `remote_application_ids` | `array` | Provider's list of candidate application IDs | +| `hired_at` | `string` | Candidate hired date | +| `country` | `string` | Candidate country | +| `custom_fields` | `array` | The candidate custom fields | +| `tags` | `array` | List of candidate tags indicating metadata associated with the candidate | +| `created_at` | `string` | Candidate created date | +| `updated_at` | `string` | Candidate updated date | + +### `CandidateEmail` +| Field | Type | Description | +|-------|------|-------------| +| `type` | `string` | Type of the email | +| `value` | `string` | Email value | + +### `ChangesActor` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | + +### `Content` +| Field | Type | Description | +|-------|------|-------------| +| `url` | `string` | URL where the file content is located | +| `unified_url` | `string` | Unified download URL for retrieving file content. | +| `file_format` | `FileFormatEnum` | The file format of the file | + +### `CreateAnswer` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `type` | `AnswerEnum` | Type of the answer | +| `values` | `array` | Values of the answer | + +### `CreateCandidate` +| Field | Type | Description | +|-------|------|-------------| +| `passthrough` | `object` | Value to pass through to the provider | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `phone_number` | `string` | The candidate personal phone number | +| `phone_numbers` | `array` | List of candidate phone numbers including the type of the number when available | +| `name` | `string` | Candidate name | +| `first_name` | `string` | Candidate first name | +| `last_name` | `string` | Candidate last name | +| `email` | `string` | Candidate email | +| `social_links` | `array` | List of candidate social links | +| `company` | `string` | Candidate company | +| `title` | `string` | Candidate title | +| `hired_at` | `string` | Candidate hired date | +| `country` | `string` | Candidate country | +| `custom_fields` | `array` | The candidate custom fields | + +### `CreatePackage` +| Field | Type | Description | +|-------|------|-------------| +| `name` | `string` | Package name | +| `description` | `string` | Package description | + +### `CreateQuestionnaire` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `answers` | `array` | | + +### `CreateSource` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | The source of the application | + +### `CustomFieldDefinition` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `description` | `string` | | +| `type` | `CustomFieldTypeEnum` | The type of the custom field. | +| `options` | `array` | An array of possible options for the custom field. | + +### `CustomFieldDefinitionResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `data` | `CustomFieldDefinition` | | +| `raw` | `array` | | + +### `CustomFieldOption` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique identifier for the option to be used when updating the custom field | +| `value` | `object` | The human readable value of the option | + +### `CustomFields` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the custom field. | +| `value` | `object` | The value associated with the custom field. | +| `value_id` | `string` | The unique identifier for the value of the custom field. | +| `remote_value_id` | `string` | Provider's unique identifier for the value of the custom field. | + +### `Department` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | | + +### `Field` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `label` | `string` | The label of the field | +| `type` | `string` | The type of the field | +| `values` | `array` | The possible values for the field | +| `required` | `object` | Indicates if the field is required | + +### `FileCategoryEnumApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | The category of the file | +| `source_value` | `object` | | + +### `Interview` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `application_id` | `string` | | +| `remote_application_id` | `string` | Provider's unique identifier of the application | +| `interview_stage_id` | `string` | | +| `remote_interview_stage_id` | `string` | Provider's unique identifier of the interview stage | +| `interview_stage` | `InterviewStage` | | +| `interview_status` | `InterviewStatusEnum` | | +| `interviewer_ids` | `array` | | +| `remote_interviewer_ids` | `array` | Provider's unique identifiers of the interviewers | +| `interview_parts` | `array` | | +| `interviewers` | `array` | | +| `start_at` | `string` | Interview start date | +| `end_at` | `string` | Interview end date | +| `meeting_url` | `string` | | +| `created_at` | `string` | Interview created date | +| `updated_at` | `string` | Interview updated date | + +### `InterviewPart` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `type` | `InterviewTypeEnum` | | +| `title` | `string` | The title of interview, usually corresponding to the title of an associated calendar event | +| `interviewer_ids` | `array` | The user (interviewer) IDs taking part in this specific interview. | +| `remote_interviewer_ids` | `array` | Provider's user (interviewer) IDs taking part in this specific interview. | +| `meeting_url` | `string` | The meeting URL for the interview - this may be populated using the underlying location if the lo... | +| `meeting_provider` | `string` | The video meeting provider used for the interview. | +| `start_at` | `string` | The specific interview part's start date | +| `end_at` | `string` | The specific interview part's end date | +| `created_at` | `string` | Interview part created date | +| `updated_at` | `string` | Interview part updated date | + +### `InterviewStage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | | +| `order` | `number` | | +| `created_at` | `string` | Interview Stage created date | +| `updated_at` | `string` | Interview Stage updated date | + +### `Interviewer` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `first_name` | `string` | | +| `last_name` | `string` | | +| `name` | `string` | | +| `email` | `string` | | + +### `JobPosting` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `title` | `string` | | +| `locations` | `array` | | +| `internal` | `string` | | +| `status` | `JobPostingStatusEnum` | | +| `job_id` | `string` | | +| `remote_job_id` | `string` | Provider's unique identifier of the job | +| `content` | `JobPostingContent` | | +| `compensation` | `array` | | +| `employment_type` | `EmploymentTypeEnum` | | +| `employment_contract_type` | `EmploymentContractTypeEnum` | | +| `external_url` | `string` | | +| `external_apply_url` | `string` | | +| `questionnaires` | `array` | | +| `start_date` | `string` | The posting start date | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `JobPostingCompensation` +| Field | Type | Description | +|-------|------|-------------| +| `title` | `string` | | +| `type` | `CompensationTypeEnum` | | +| `pay_period` | `PayPeriodEnum` | | +| `pay_frequency` | `PayFrequencyEnum` | | +| `currency` | `string` | | +| `value` | `string` | | +| `min_value_range` | `string` | | +| `max_value_range` | `string` | | + +### `JobPostingContent` +| Field | Type | Description | +|-------|------|-------------| +| `plain` | `string` | | +| `html` | `string` | | +| `sections` | `array` | | + +### `JobPostingContentSection` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `type` | `AssessmentTypeEnum` | | +| `label` | `string` | | +| `content` | `string` | | + +### `JobPostingLocation` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | + +### `JobPostingQuestionnaire` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `internal` | `object` | | +| `questions` | `array` | | + +### `List` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | | +| `items` | `array` | | +| `created_at` | `string` | Timestamp when the list was created | +| `updated_at` | `string` | Timestamp when the list was last updated | +| `type` | `ListTypeEnum` | The list type | + +### `ListItem` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | + +### `Note` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `content` | `array` | | +| `author_id` | `string` | Unique identifier of the author | +| `remote_author_id` | `string` | Provider's unique identifier of the author | +| `visibility` | `NotesVisibilityEnum` | Visibility of the note | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `deleted_at` | `string` | Date of Deletion | + +### `NoteContentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `body` | `string` | Body of the note | + +### `Offer` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `application_id` | `string` | | +| `remote_application_id` | `string` | Provider's unique identifier of the application | +| `start_date` | `string` | Date of creation | +| `offer_status` | `OfferStatusEnum` | | +| `salary` | `number` | | +| `currency` | `string` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `offer_history` | `array` | | + +### `OfferHistory` +| Field | Type | Description | +|-------|------|-------------| +| `start_date` | `string` | Start Date of the offer | +| `salary` | `number` | | +| `currency` | `string` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `OrderApplicationApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `application_status` | `ApplicationStatusEnum` | | +| `passthrough` | `object` | Value to pass through to the provider | + +### `OrderBackgroundCheckPackageApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | +| `tests` | `array` | Package tests | + +### `OrderCandidateApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `first_name` | `string` | Candidate first name | +| `last_name` | `string` | Candidate last name | +| `emails` | `array` | List of candidate emails | +| `passthrough` | `object` | Value to pass through to the provider | +| `profile_url` | `string` | Candidate profile url | + +### `OrderJobApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `title` | `string` | Title of the job | +| `hiring_team` | `array` | Hiring team for the job. | +| `passthrough` | `object` | Value to pass through to the provider | + +### `OrderJobHiringTeamApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `user_id` | `string` | User ID of the hiring team member. | +| `remote_user_id` | `string` | Provider's unique identifier of the user | +| `first_name` | `string` | First name of the hiring team member. | +| `last_name` | `string` | Last name of the hiring team member. | +| `email` | `string` | Email of the hiring team member. | +| `role` | `string` | Role of the hiring team member. | + +### `OrderPackageApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | + +### `Package` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | + +### `ParentQuestion` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `option_ids` | `array` | List of parent questions's option IDs | +| `remote_option_ids` | `array` | Provider's list of parent questions's option IDs | +| `condition_type` | `ConditionTypeEnum` | | + +### `PhoneNumber` +| Field | Type | Description | +|-------|------|-------------| +| `type` | `string` | Type of phone number | +| `phone` | `string` | Phone number string | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `ProviderErrorApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | | +| `url` | `string` | | +| `raw` | `object` | | +| `headers` | `object` | | + +### `Question` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `type` | `QuestionsTypeEnum` | | +| `text` | `string` | | +| `required` | `object` | | +| `multiple_choice_answers` | `array` | | +| `parent_question` | `ParentQuestion` | | + +### `QuestionMultipleChoiceAnswers` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `text` | `string` | | + +### `Questionnaire` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `answers` | `array` | | + +### `Reference` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The reference id | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The reference name | +| `active` | `object` | The reference status | + +### `RejectedReason` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `label` | `string` | The label of the rejected reason. | +| `type` | `string` | The string type of the rejected reason. | +| `rejected_reason_type` | `RejectedReasonTypeEnum` | | + +### `ResultLink` +| Field | Type | Description | +|-------|------|-------------| +| `label` | `string` | The label of the result link. | +| `url` | `string` | The URL of the result link. | + +### `ScheduledInterview` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `application_id` | `string` | | +| `remote_application_id` | `string` | Provider's unique identifier of the application | +| `interview_stage_id` | `string` | | +| `remote_interview_stage_id` | `string` | Provider's unique identifier of the interview stage | +| `interview_stage` | `InterviewStage` | | +| `interview_status` | `InterviewStatusEnum` | | +| `interviewer_ids` | `array` | | +| `remote_interviewer_ids` | `array` | Provider's unique identifiers of the interviewers | +| `interview_parts` | `array` | | +| `interviewers` | `array` | | +| `start_at` | `string` | Interview start date | +| `end_at` | `string` | Interview end date | +| `meeting_url` | `string` | | +| `created_at` | `string` | Interview created date | +| `updated_at` | `string` | Interview updated date | + +### `ScoreApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `label` | `string` | The label of the score | +| `value` | `string` | The value is the actual score | +| `min` | `string` | The minimum value of the score | +| `max` | `string` | The maximum value of the score | + +### `Scorecard` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `sections` | `array` | The sections in the scorecard | +| `label` | `string` | The label of the scorecard | +| `candidate_id` | `string` | The candidate ID associated with the scorecard | +| `remote_candidate_id` | `string` | Provider's unique identifier of the candidate | +| `application_id` | `string` | The application ID associated with the scorecard | +| `remote_application_id` | `string` | Provider's unique identifier of the application | +| `interview_id` | `string` | The interview ID associated with the scorecard | +| `remote_interview_id` | `string` | Provider's unique identifier of the interview | +| `author_id` | `string` | The author ID of the scorecard | +| `remote_author_id` | `string` | Provider's unique identifier of the author | +| `overall_recommendation` | `string` | The overall recommendation | +| `created_at` | `string` | The creation date of the scorecard | +| `updated_at` | `string` | The update date of the scorecard | + +### `ScorecardSection` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `label` | `string` | The label of the section | +| `fields` | `array` | The fields within the section | + +### `SocialLink` +| Field | Type | Description | +|-------|------|-------------| +| `type` | `string` | Type of the social link | +| `url` | `string` | URL of the social link | + +### `Source` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The source of the application | + +### `Tag` +| Field | Type | Description | +|-------|------|-------------| +| `remote_id` | `string` | Remote ID of the tag | +| `name` | `string` | Name of the tag | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +### `UnifiedWarningApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `message` | `string` | | + +### `UpdatePackage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | Package name | +| `description` | `string` | Package description | + +### `User` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `email` | `string` | | +| `first_name` | `string` | | +| `last_name` | `string` | | +| `name` | `string` | | +| `phone` | `string` | | + +### `WriteResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | | +| `message` | `string` | | +| `timestamp` | `string` | | +| `provider_errors` | `array` | | +| `unified_warnings` | `array` | | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `AnswerEnum` | `short_text`, `long_text`, `attachment`, `multi_select`, `single_select`, `boolean`, `number`, `date`, `video`, `reference_check`, `url` | +| `ApplicationChangeTypeEnum` | `application_status`, `interview_stage`, `rejected_reasons` | +| `ApplicationStatusEnum` | `active`, `assessment`, `background_check`, `converted`, `declined_by_candidate`, `hired`, `interview`, `lead`, `offer`, `reference_check`, `rejected`, `review`, `screen`, `new`, `onboarding`, `created`, `accepted`, `short_list`, `approved` | +| `AssessmentTypeEnum` | `responsibilities`, `skills`, `benefits`, `company_overview`, `description`, `other` | +| `AtsDocumentTypeEnum` | `resume`, `avatar`, `cover_letter`, `profile_picture`, `policy`, `passport`, `assessment`, `interview_attachment`, `take_home_test`, `offer_letter`, `signed_offer_letter`, `national_id`, `offer_packet`, `other` | +| `AtsDocumentsUploadCategoryEnumApiModel` | `resume`, `avatar`, `cover_letter`, `profile_picture`, `policy`, `passport`, `assessment`, `interview_attachment`, `take_home_test`, `offer_letter`, `signed_offer_letter`, `national_id`, `offer_packet`, `other` | +| `AtsJobStatusEnum` | `published`, `draft`, `pending`, `internal`, `archived`, `closed`, `open`, `deleted`, `on_hold` | +| `AttachmentContentType` | `text`, `pdf`, `video`, `other` | +| `CompensationTypeEnum` | `salary`, `hourly`, `commission`, `bonus`, `equity`, `other` | +| `ConditionTypeEnum` | `equals_to`, `contains` | +| `ConfidentialEnumApiModel` | `true`, `false` | +| `CustomFieldTypeEnum` | `date`, `float`, `integer`, `list`, `checkbox`, `text`, `boolean`, `single_select`, `multi_select`, `url`, `other` | +| `EmploymentContractTypeEnum` | `full_time`, `shifts`, `part_time` | +| `EmploymentTypeEnum` | `contractor`, `intern`, `permanent`, `apprentice`, `freelance`, `terminated`, `temporary`, `seasonal`, `volunteer`, `probation`, `internal`, `external`, `expatriate`, `employer_of_record`, `casual`, `Programme` | +| `FileFormatEnum` | `ez`, `aw`, `atom`, `atomcat`, `atomdeleted`, `atomsvc`, `dwd`, `held`, `rsat`, `bdoc`, `xcs`, `ccxml`, `cdfx`, `cdmia`, `cdmic`, `cdmid`, `cdmio`, `cdmiq`, `cu`, `mpd`, `davmount`, `dbk`, `dssc`, `xdssc`, `es`, `ecma`, `emma`, `emotionml`, `epub`, `exi`, `exp`, `fdt`, `pfr`, `geojson`, `gml`, `gpx`, `gxf`, `gz`, `hjson`, `stk`, `ink`, `inkml`, `ipfix`, `its`, `jar`, `war`, `ear`, `ser`, `class`, `js`, `mjs`, `json`, `map`, `json5`, `jsonml`, `jsonld`, `lgr`, `lostxml`, `hqx`, `cpt`, `mads`, `webmanifest`, `mrc`, `mrcx`, `ma`, `nb`, `mb`, `mathml`, `mbox`, `mscml`, `metalink`, `meta4`, `mets`, `maei`, `musd`, `mods`, `m21`, `mp21`, `mp4s`, `m4p`, `doc`, `dot`, `mxf`, `nq`, `nt`, `cjs`, `bin`, `dms`, `lrf`, `mar`, `so`, `dist`, `distz`, `pkg`, `bpk`, `dump`, `elc`, `deploy`, `exe`, `dll`, `deb`, `dmg`, `iso`, `img`, `msi`, `msp`, `msm`, `buffer`, `oda`, `opf`, `ogx`, `omdoc`, `onetoc`, `onetoc2`, `onetmp`, `onepkg`, `oxps`, `relo`, `xer`, `pdf`, `pgp`, `asc`, `sig`, `prf`, `p10`, `p7m`, `p7c`, `p7s`, `p8`, `ac`, `cer`, `crl`, `pkipath`, `pki`, `pls`, `ai`, `eps`, `ps`, `provx`, `pskcxml`, `raml`, `rdf`, `owl`, `rif`, `rnc`, `rl`, `rld`, `rs`, `rapd`, `sls`, `rusd`, `gbr`, `mft`, `roa`, `rsd`, `rss`, `rtf`, `sbml`, `scq`, `scs`, `spq`, `spp`, `sdp`, `senmlx`, `sensmlx`, `setpay`, `setreg`, `shf`, `siv`, `sieve`, `smi`, `smil`, `rq`, `srx`, `gram`, `grxml`, `sru`, `ssdl`, `ssml`, `swidtag`, `tei`, `teicorpus`, `tfi`, `tsd`, `toml`, `trig`, `ttml`, `ubj`, `rsheet`, `td`, `vxml`, `wasm`, `wgt`, `hlp`, `wsdl`, `wspolicy`, `xaml`, `xav`, `xca`, `xdf`, `xel`, `xns`, `xenc`, `xhtml`, `xht`, `xlf`, `xml`, `xsl`, `xsd`, `rng`, `dtd`, `xop`, `xpl`, `*xsl`, `xslt`, `xspf`, `mxml`, `xhvml`, `xvml`, `xvm`, `yang`, `yin`, `zip`, `*3gpp`, `adp`, `amr`, `au`, `snd`, `mid`, `midi`, `kar`, `rmi`, `mxmf`, `*mp3`, `m4a`, `mp4a`, `mpga`, `mp2`, `mp2a`, `mp3`, `m2a`, `m3a`, `oga`, `ogg`, `spx`, `opus`, `s3m`, `sil`, `wav`, `*wav`, `weba`, `xm`, `ttc`, `otf`, `ttf`, `woff`, `woff2`, `exr`, `apng`, `avif`, `bmp`, `cgm`, `drle`, `emf`, `fits`, `g3`, `gif`, `heic`, `heics`, `heif`, `heifs`, `hej2`, `hsj2`, `ief`, `jls`, `jp2`, `jpg2`, `jpeg`, `jpg`, `jpe`, `jph`, `jhc`, `jpm`, `jpx`, `jpf`, `jxr`, `jxra`, `jxrs`, `jxs`, `jxsc`, `jxsi`, `jxss`, `ktx`, `ktx2`, `png`, `sgi`, `svg`, `svgz`, `t38`, `tif`, `tiff`, `tfx`, `webp`, `wmf`, `disposition-notification`, `u8msg`, `u8dsn`, `u8mdn`, `u8hdr`, `eml`, `mime`, `3mf`, `gltf`, `glb`, `igs`, `iges`, `msh`, `mesh`, `silo`, `mtl`, `obj`, `stpx`, `stpz`, `stpxz`, `stl`, `wrl`, `vrml`, `*x3db`, `x3dbz`, `x3db`, `*x3dv`, `x3dvz`, `x3d`, `x3dz`, `x3dv`, `appcache`, `manifest`, `ics`, `ifb`, `coffee`, `litcoffee`, `css`, `csv`, `html`, `htm`, `shtml`, `jade`, `jsx`, `less`, `markdown`, `md`, `mml`, `mdx`, `n3`, `txt`, `text`, `conf`, `def`, `list`, `log`, `in`, `ini`, `rtx`, `*rtf`, `sgml`, `sgm`, `shex`, `slim`, `slm`, `spdx`, `stylus`, `styl`, `tsv`, `t`, `tr`, `roff`, `man`, `me`, `ms`, `ttl`, `uri`, `uris`, `urls`, `vcard`, `vtt`, `*xml`, `yaml`, `yml`, `3gp`, `3gpp`, `3g2`, `h261`, `h263`, `h264`, `m4s`, `jpgv`, `*jpm`, `jpgm`, `mj2`, `mjp2`, `ts`, `mp4`, `mp4v`, `mpg4`, `mpeg`, `mpg`, `mpe`, `m1v`, `m2v`, `ogv`, `qt`, `mov`, `webm`, `cww`, `1km`, `plb`, `psb`, `pvb`, `tcap`, `pwn`, `aso`, `imp`, `acu`, `atc`, `acutc`, `air`, `fcdt`, `fxp`, `fxpl`, `xdp`, `xfdf`, `ahead`, `azf`, `azs`, `azw`, `acc`, `ami`, `apk`, `cii`, `fti`, `atx`, `mpkg`, `key`, `m3u8`, `numbers`, `pages`, `pkpass`, `swi`, `iota`, `aep`, `bmml`, `mpm`, `bmi`, `rep`, `cdxml`, `mmd`, `cdy`, `csl`, `cla`, `rp9`, `c4g`, `c4d`, `c4f`, `c4p`, `c4u`, `c11amc`, `c11amz`, `csp`, `cdbcmsg`, `cmc`, `clkx`, `clkk`, `clkp`, `clkt`, `clkw`, `wbs`, `pml`, `ppd`, `car`, `pcurl`, `dart`, `rdz`, `dbf`, `uvf`, `uvvf`, `uvd`, `uvvd`, `uvt`, `uvvt`, `uvx`, `uvvx`, `uvz`, `uvvz`, `fe_launch`, `dna`, `mlp`, `mle`, `dpg`, `dfac`, `kpxx`, `ait`, `svc`, `geo`, `mag`, `nml`, `esf`, `msf`, `qam`, `slt`, `ssf`, `es3`, `et3`, `ez2`, `ez3`, `fdf`, `mseed`, `seed`, `dataless`, `gph`, `ftc`, `fm`, `frame`, `maker`, `book`, `fnc`, `ltf`, `fsc`, `oas`, `oa2`, `oa3`, `fg5`, `bh2`, `ddd`, `xdw`, `xbd`, `fzs`, `txd`, `ggb`, `ggt`, `gex`, `gre`, `gxt`, `g2w`, `g3w`, `gmx`, `gdoc`, `gslides`, `gsheet`, `kml`, `kmz`, `gqf`, `gqs`, `gac`, `ghf`, `gim`, `grv`, `gtm`, `tpl`, `vcg`, `hal`, `zmm`, `hbci`, `les`, `hpgl`, `hpid`, `hps`, `jlt`, `pcl`, `pclxl`, `sfd-hdstx`, `mpy`, `afp`, `listafp`, `list3820`, `irm`, `sc`, `icc`, `icm`, `igl`, `ivp`, `ivu`, `igm`, `xpw`, `xpx`, `i2g`, `qbo`, `qfx`, `rcprofile`, `irp`, `xpr`, `fcs`, `jam`, `rms`, `jisp`, `joda`, `ktz`, `ktr`, `karbon`, `chrt`, `kfo`, `flw`, `kon`, `kpr`, `kpt`, `ksp`, `kwd`, `kwt`, `htke`, `kia`, `kne`, `knp`, `skp`, `skd`, `skt`, `skm`, `sse`, `lasxml`, `lbd`, `lbe`, `apr`, `pre`, `nsf`, `org`, `scm`, `lwp`, `portpkg`, `mvt`, `mcd`, `mc1`, `cdkey`, `mwf`, `mfm`, `flo`, `igx`, `mif`, `daf`, `dis`, `mbk`, `mqy`, `msl`, `plc`, `txf`, `mpn`, `mpc`, `xul`, `cil`, `cab`, `xls`, `xlm`, `xla`, `xlc`, `xlt`, `xlw`, `xlam`, `xlsb`, `xlsm`, `xltm`, `eot`, `chm`, `ims`, `lrm`, `thmx`, `msg`, `cat`, `*stl`, `ppt`, `pps`, `pot`, `ppam`, `pptm`, `sldm`, `ppsm`, `potm`, `mpp`, `mpt`, `docm`, `dotm`, `wps`, `wks`, `wcm`, `wdb`, `wpl`, `xps`, `mseq`, `mus`, `msty`, `taglet`, `nlu`, `ntf`, `nitf`, `nnd`, `nns`, `nnw`, `*ac`, `ngdat`, `n-gage`, `rpst`, `rpss`, `edm`, `edx`, `ext`, `odc`, `otc`, `odb`, `odf`, `odft`, `odg`, `otg`, `odi`, `oti`, `odp`, `otp`, `ods`, `ots`, `odt`, `odm`, `ott`, `oth`, `xo`, `dd2`, `obgx`, `oxt`, `osm`, `pptx`, `sldx`, `ppsx`, `potx`, `xlsx`, `xltx`, `docx`, `dotx`, `mgp`, `dp`, `esa`, `pdb`, `pqa`, `oprc`, `paw`, `str`, `ei6`, `efif`, `wg`, `plf`, `pbd`, `box`, `mgz`, `qps`, `ptid`, `qxd`, `qxt`, `qwd`, `qwt`, `qxl`, `qxb`, `rar`, `bed`, `mxl`, `musicxml`, `cryptonote`, `cod`, `rm`, `rmvb`, `link66`, `st`, `see`, `sema`, `semd`, `semf`, `ifm`, `itp`, `iif`, `ipk`, `twd`, `twds`, `mmf`, `teacher`, `fo`, `sdkm`, `sdkd`, `dxp`, `sfs`, `sdc`, `sda`, `sdd`, `smf`, `sdw`, `vor`, `sgl`, `smzip`, `sm`, `wadl`, `sxc`, `stc`, `sxd`, `std`, `sxi`, `sti`, `sxm`, `sxw`, `sxg`, `stw`, `sus`, `susp`, `svd`, `sis`, `sisx`, `xsm`, `bdm`, `xdm`, `ddf`, `tao`, `pcap`, `cap`, `dmp`, `tmo`, `tpt`, `mxs`, `tra`, `ufd`, `ufdl`, `utz`, `umj`, `unityweb`, `uoml`, `vcx`, `vsd`, `vst`, `vss`, `vsw`, `vis`, `vsf`, `wbxml`, `wmlc`, `wmlsc`, `wtb`, `nbp`, `wpd`, `wqd`, `stf`, `xar`, `xfdl`, `hvd`, `hvs`, `hvp`, `osf`, `osfpvg`, `saf`, `spf`, `cmp`, `zir`, `zirz`, `zaz`, `7z`, `abw`, `ace`, `*dmg`, `arj`, `aab`, `x32`, `u32`, `vox`, `aam`, `aas`, `bcpio`, `*bdoc`, `torrent`, `blb`, `blorb`, `bz`, `bz2`, `boz`, `cbr`, `cba`, `cbt`, `cbz`, `cb7`, `vcd`, `cfs`, `chat`, `pgn`, `crx`, `cco`, `nsc`, `cpio`, `csh`, `*deb`, `udeb`, `dgc`, `dir`, `dcr`, `dxr`, `cst`, `cct`, `cxt`, `w3d`, `fgd`, `swa`, `wad`, `ncx`, `dtb`, `res`, `dvi`, `evy`, `eva`, `bdf`, `gsf`, `psf`, `pcf`, `snf`, `pfa`, `pfb`, `pfm`, `afm`, `arc`, `spl`, `gca`, `ulx`, `gnumeric`, `gramps`, `gtar`, `hdf`, `php`, `install`, `*iso`, `*key`, `*numbers`, `*pages`, `jardiff`, `jnlp`, `kdbx`, `latex`, `luac`, `lzh`, `lha`, `run`, `mie`, `prc`, `mobi`, `application`, `lnk`, `wmd`, `wmz`, `xbap`, `mdb`, `obd`, `crd`, `clp`, `*exe`, `*dll`, `com`, `bat`, `*msi`, `mvb`, `m13`, `m14`, `*wmf`, `*wmz`, `*emf`, `emz`, `mny`, `pub`, `scd`, `trm`, `wri`, `nc`, `cdf`, `pac`, `nzb`, `pl`, `pm`, `*prc`, `*pdb`, `p12`, `pfx`, `p7b`, `spc`, `p7r`, `*rar`, `rpm`, `ris`, `sea`, `sh`, `shar`, `swf`, `xap`, `sql`, `sit`, `sitx`, `srt`, `sv4cpio`, `sv4crc`, `t3`, `gam`, `tar`, `tcl`, `tk`, `tex`, `tfm`, `texinfo`, `texi`, `*obj`, `ustar`, `hdd`, `ova`, `ovf`, `vbox`, `vbox-extpack`, `vdi`, `vhd`, `vmdk`, `src`, `webapp`, `der`, `crt`, `pem`, `fig`, `*xlf`, `xpi`, `xz`, `z1`, `z2`, `z3`, `z4`, `z5`, `z6`, `z7`, `z8`, `uva`, `uvva`, `eol`, `dra`, `dts`, `dtshd`, `lvp`, `pya`, `ecelp4800`, `ecelp7470`, `ecelp9600`, `rip`, `aac`, `aif`, `aiff`, `aifc`, `caf`, `flac`, `*m4a`, `mka`, `m3u`, `wax`, `wma`, `ram`, `ra`, `rmp`, `*ra`, `cdx`, `cif`, `cmdf`, `cml`, `csml`, `xyz`, `btif`, `pti`, `psd`, `azv`, `uvi`, `uvvi`, `uvg`, `uvvg`, `djvu`, `djv`, `*sub`, `dwg`, `dxf`, `fbs`, `fpx`, `fst`, `mmr`, `rlc`, `ico`, `dds`, `mdi`, `wdp`, `npx`, `b16`, `tap`, `vtf`, `wbmp`, `xif`, `pcx`, `3ds`, `ras`, `cmx`, `fh`, `fhc`, `fh4`, `fh5`, `fh7`, `*ico`, `jng`, `sid`, `*bmp`, `*pcx`, `pic`, `pct`, `pnm`, `pbm`, `pgm`, `ppm`, `rgb`, `tga`, `xbm`, `xpm`, `xwd`, `wsc`, `dae`, `dwf`, `gdl`, `gtw`, `mts`, `ogex`, `x_b`, `x_t`, `vds`, `usdz`, `bsp`, `vtu`, `dsc`, `curl`, `dcurl`, `mcurl`, `scurl`, `sub`, `fly`, `flx`, `gv`, `3dml`, `spot`, `jad`, `wml`, `wmls`, `s`, `asm`, `c`, `cc`, `cxx`, `cpp`, `h`, `hh`, `dic`, `htc`, `f`, `for`, `f77`, `f90`, `hbs`, `java`, `lua`, `mkd`, `nfo`, `opml`, `*org`, `p`, `pas`, `pde`, `sass`, `scss`, `etx`, `sfv`, `ymp`, `uu`, `vcs`, `vcf`, `uvh`, `uvvh`, `uvm`, `uvvm`, `uvp`, `uvvp`, `uvs`, `uvvs`, `uvv`, `uvvv`, `dvb`, `fvt`, `mxu`, `m4u`, `pyv`, `uvu`, `uvvu`, `viv`, `f4v`, `fli`, `flv`, `m4v`, `mkv`, `mk3d`, `mks`, `mng`, `asf`, `asx`, `vob`, `wm`, `wmv`, `wmx`, `wvx`, `avi`, `movie`, `smv`, `ice`, `mht` | +| `InterviewStatusEnum` | `unscheduled`, `scheduled`, `completed`, `cancelled`, `pending_feedback` | +| `InterviewTypeEnum` | `screening`, `lunch`, `on_site`, `presentation`, `sell`, `culture`, `informal`, `test`, `phone`, `video` | +| `JobPostingStatusEnum` | `live`, `draft`, `pending`, `internal`, `rejected`, `closed`, `archived` | +| `ListTypeEnum` | `candidates`, `contacts`, `companies` | +| `NotesVisibilityEnum` | `private`, `public` | +| `OfferStatusEnum` | `pending`, `retracted`, `accepted`, `rejected`, `created`, `approved`, `not_approved` | +| `PayFrequencyEnum` | `hourly`, `weekly`, `bi_weekly`, `four_weekly`, `semi_monthly`, `monthly`, `bi_monthly`, `quarterly`, `semi_annually`, `yearly`, `thirteen_monthly`, `pro_rata`, `half_yearly`, `daily` | +| `PayPeriodEnum` | `hour`, `day`, `week`, `every_two_weeks`, `month`, `twice_a_month`, `every_two_months`, `quarter`, `every_six_months`, `year`, `one_off`, `none` | +| `QuestionsTypeEnum` | `short_text`, `long_text`, `attachment`, `multi_select`, `single_select`, `boolean`, `number`, `date`, `video`, `reference_check`, `url` | +| `RejectedReasonTypeEnum` | `rejected_by_candidate`, `rejected_by_organization`, `other`, `unknown` | +| `ResultEnum` | `initiated`, `in_progress`, `cancelled`, `completed`, `expired`, `failed`, `passed` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/crm.md b/.claude/plugins/stackone-connector-builder/references/schemas/crm.md new file mode 100644 index 0000000..dc992dc --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/crm.md @@ -0,0 +1,150 @@ +# CRM (Customer Relationship Management) — Schema Reference + +> Manage contacts, accounts, lists and activities. + +> **OAS source:** `https://docs.stackone.com/crm/api-reference/crm.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/crm/contacts` | List Contacts | +| `POST` | `/unified/crm/contacts` | Create Contact | +| `GET` | `/unified/crm/contacts/{id}` | Get Contact | +| `PATCH` | `/unified/crm/contacts/{id}` | Update Contact (early access) | +| `GET` | `/unified/crm/accounts` | List Accounts | +| `GET` | `/unified/crm/accounts/{id}` | Get Account | +| `GET` | `/unified/crm/lists` | Get all Lists | +| `GET` | `/unified/crm/lists/{id}` | Get List | +| `GET` | `/unified/crm/custom_field_definitions/contacts` | List Contact Custom Field Definitions | +| `GET` | `/unified/crm/custom_field_definitions/contacts/{id}` | Get Contact Custom Field Definition | + +--- + +## Models + +### `Account` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `owner_id` | `string` | | +| `remote_owner_id` | `string` | Provider's unique identifier of the owner | +| `name` | `string` | | +| `description` | `string` | | +| `industries` | `array` | Values of the industries | +| `annual_revenue` | `string` | | +| `website` | `string` | | +| `addresses` | `array` | | +| `phone_numbers` | `array` | List of account phone numbers | +| `created_at` | `string` | Timestamp when the account was created | +| `updated_at` | `string` | Timestamp when the account was last updated | + +### `AccountAddress` +| Field | Type | Description | +|-------|------|-------------| +| `street_1` | `string` | | +| `street_2` | `string` | | +| `city` | `string` | | +| `state` | `string` | | +| `zip_code` | `string` | | +| `country` | `CountryEnum` | The country code | +| `location_type` | `LocationTypeEnum` | The location type | + +### `Contact` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `first_name` | `string` | The contact first name | +| `last_name` | `string` | The contact last name | +| `company_name` | `string` | The contact company name | +| `emails` | `array` | List of contact email addresses | +| `phone_numbers` | `array` | List of contact phone numbers | +| `deal_ids` | `array` | List of associated deal IDs | +| `remote_deal_ids` | `array` | Provider's list of associated deal IDs | +| `account_ids` | `array` | List of associated account IDs | +| `remote_account_ids` | `array` | Provider's list of associated account IDs | +| `custom_fields` | `array` | Contact custom fields | +| `created_at` | `string` | Timestamp when the contact was created | +| `updated_at` | `string` | Timestamp when the contact was last updated | + +### `CustomFieldDefinition` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `description` | `string` | | +| `type` | `CustomFieldTypeEnum` | The type of the custom field. | +| `options` | `array` | An array of possible options for the custom field. | + +### `CustomFieldDefinitionResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `data` | `CustomFieldDefinition` | | +| `raw` | `array` | | + +### `CustomFieldOption` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique identifier for the option to be used when updating the custom field | +| `value` | `object` | The human readable value of the option | + +### `CustomFields` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the custom field. | +| `value` | `object` | The value associated with the custom field. | +| `value_id` | `string` | The unique identifier for the value of the custom field. | +| `remote_value_id` | `string` | Provider's unique identifier for the value of the custom field. | + +### `List` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | | +| `items` | `array` | | +| `created_at` | `string` | Timestamp when the list was created | +| `updated_at` | `string` | Timestamp when the list was last updated | +| `type` | `ListTypeEnum` | The list type | + +### `ListItem` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `CountryEnum` | `AF`, `AL`, `DZ`, `AS`, `AD`, `AO`, `AI`, `AQ`, `AG`, `AR`, `AM`, `AW`, `AU`, `AT`, `AZ`, `BS`, `BH`, `BD`, `BB`, `BY`, `BE`, `BZ`, `BJ`, `BM`, `BT`, `BO`, `BQ`, `BA`, `BW`, `BV`, `BR`, `IO`, `BN`, `BG`, `BF`, `BI`, `KH`, `CM`, `CA`, `CV`, `KY`, `CF`, `TD`, `CL`, `CN`, `CX`, `CC`, `CO`, `KM`, `CG`, `CD`, `CK`, `CR`, `HR`, `CU`, `CW`, `CY`, `CZ`, `CI`, `DK`, `DJ`, `DM`, `DO`, `EC`, `EG`, `SV`, `GQ`, `ER`, `EE`, `ET`, `FK`, `FO`, `FJ`, `FI`, `FR`, `GF`, `PF`, `TF`, `GA`, `GM`, `GE`, `DE`, `GH`, `GI`, `GR`, `GL`, `GD`, `GP`, `GU`, `GT`, `GG`, `GN`, `GW`, `GY`, `HT`, `HM`, `VA`, `HN`, `HK`, `HU`, `IS`, `IN`, `ID`, `IR`, `IQ`, `IE`, `IM`, `IL`, `IT`, `JM`, `JP`, `JE`, `JO`, `KZ`, `KE`, `KI`, `KP`, `KR`, `KW`, `KG`, `LA`, `LV`, `LB`, `LS`, `LR`, `LY`, `LI`, `LT`, `LU`, `MO`, `MK`, `MG`, `MW`, `MY`, `MV`, `ML`, `MT`, `MH`, `MQ`, `MR`, `MU`, `YT`, `MX`, `FM`, `MD`, `MC`, `MN`, `ME`, `MS`, `MA`, `MZ`, `MM`, `NA`, `NR`, `NP`, `NL`, `NC`, `NZ`, `NI`, `NE`, `NG`, `NU`, `NF`, `MP`, `NO`, `OM`, `PK`, `PW`, `PS`, `PA`, `PG`, `PY`, `PE`, `PH`, `PN`, `PL`, `PT`, `PR`, `QA`, `RO`, `RU`, `RW`, `RE`, `BL`, `SH`, `KN`, `LC`, `MF`, `PM`, `VC`, `WS`, `SM`, `ST`, `SA`, `SN`, `RS`, `SC`, `SL`, `SG`, `SX`, `SK`, `SI`, `SB`, `SO`, `ZA`, `GS`, `SS`, `ES`, `LK`, `SD`, `SR`, `SJ`, `SZ`, `SE`, `CH`, `SY`, `TW`, `TJ`, `TZ`, `TH`, `TL`, `TG`, `TK`, `TO`, `TT`, `TN`, `TR`, `TM`, `TC`, `TV`, `UG`, `UA`, `AE`, `GB`, `US`, `UM`, `UY`, `UZ`, `VU`, `VE`, `VN`, `VG`, `VI`, `WF`, `EH`, `YE`, `ZM`, `ZW` | +| `CustomFieldTypeEnum` | `date`, `float`, `integer`, `list`, `checkbox`, `text`, `boolean`, `single_select`, `multi_select`, `url`, `other` | +| `ListTypeEnum` | `candidates`, `contacts`, `companies` | +| `LocationTypeEnum` | `home`, `work` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/documents.md b/.claude/plugins/stackone-connector-builder/references/schemas/documents.md new file mode 100644 index 0000000..5ae978f --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/documents.md @@ -0,0 +1,135 @@ +# Documents — Schema Reference + +> Manage files, folders and drives. + +> **OAS source:** `https://docs.stackone.com/documents/api-reference/documents.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/documents/files/{id}/download` | Download File | +| `POST` | `/unified/documents/files/upload` | Upload File | +| `GET` | `/unified/documents/files` | List Files | +| `GET` | `/unified/documents/files/{id}` | Get File | +| `GET` | `/unified/documents/folders` | List Folders | +| `GET` | `/unified/documents/folders/{id}` | Get Folder | +| `GET` | `/unified/documents/drives` | List Drives | +| `GET` | `/unified/documents/drives/{id}` | Get Drive | + +--- + +## Models + +### `Drives` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this drive | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name associated with this drive | +| `description` | `string` | The description associated with this drive | +| `url` | `string` | The url of the drive | +| `created_at` | `string` | The created date of the drive | +| `updated_at` | `string` | The last updated date of the drive | + +### `Files` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name associated with this file | +| `description` | `string` | The description of the file | +| `size` | `number` | The size of this file | +| `url` | `string` | The url of the file | +| `file_format` | `FileFormatEnum` | The file format of the file | +| `path` | `string` | The path where the file is stored | +| `owner_id` | `string` | The user ID of owner of this file | +| `export_formats` | `array` | List of supported export formats | +| `default_download_format` | `string` | Default download format | +| `remote_owner_id` | `string` | Provider's unique identifier of the owner of this file | +| `folder_id` | `string` | The parent folder ID associated with this file | +| `remote_folder_id` | `string` | Provider's unique identifier of the parent folder associated with this file | +| `drive_id` | `string` | The parent drive ID associated with this file | +| `remote_drive_id` | `string` | Provider's unique identifier of the parent drive associated with this file | +| `created_at` | `string` | The created date of the file | +| `updated_at` | `string` | The last updated date of the file | +| `has_content` | `object` | Whether the file has content | +| `has_children` | `object` | Whether the file has children | +| `all_parent_folder_ids` | `array` | List of containing parent Folder IDs in descending order | + +### `Folders` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name associated with this folder | +| `description` | `string` | The description of the folder | +| `size` | `number` | The size of this folder in bytes | +| `url` | `string` | The url of the folder | +| `path` | `string` | The path where the folder is stored | +| `owner_id` | `string` | The user ID of owner of this folder | +| `remote_owner_id` | `string` | Provider's unique identifier of the owner of this folder | +| `parent_folder_id` | `string` | The parent folder ID associated with this folder | +| `remote_parent_folder_id` | `string` | Provider's unique identifier of the parent folder associated with this folder | +| `drive_id` | `string` | The parent drive ID associated with this folder | +| `remote_drive_id` | `string` | Provider's unique identifier of the parent drive associated with this folder | +| `created_at` | `string` | The created date of the folder | +| `updated_at` | `string` | The last updated date of the folder | +| `has_content` | `object` | Whether the folder has content | +| `has_children` | `object` | Whether the folder has children | +| `is_root` | `object` | Whether the folder is at the root level of the drive | +| `all_parent_folder_ids` | `array` | List of containing parent Folder IDs in descending order | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `ProviderErrorApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | | +| `url` | `string` | | +| `raw` | `object` | | +| `headers` | `object` | | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +### `UnifiedUploadCategoryEnumApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | The category name for associating uploaded files. | +| `source_value` | `string` | The provider specific category for associating uploaded files, if provided, the value will be ign... | + +### `UnifiedWarningApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `message` | `string` | | + +### `WriteResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | | +| `message` | `string` | | +| `timestamp` | `string` | | +| `provider_errors` | `array` | | +| `unified_warnings` | `array` | | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `ConfidentialEnumApiModel` | `true`, `false` | +| `FileFormatEnum` | `ez`, `aw`, `atom`, `atomcat`, `atomdeleted`, `atomsvc`, `dwd`, `held`, `rsat`, `bdoc`, `xcs`, `ccxml`, `cdfx`, `cdmia`, `cdmic`, `cdmid`, `cdmio`, `cdmiq`, `cu`, `mpd`, `davmount`, `dbk`, `dssc`, `xdssc`, `es`, `ecma`, `emma`, `emotionml`, `epub`, `exi`, `exp`, `fdt`, `pfr`, `geojson`, `gml`, `gpx`, `gxf`, `gz`, `hjson`, `stk`, `ink`, `inkml`, `ipfix`, `its`, `jar`, `war`, `ear`, `ser`, `class`, `js`, `mjs`, `json`, `map`, `json5`, `jsonml`, `jsonld`, `lgr`, `lostxml`, `hqx`, `cpt`, `mads`, `webmanifest`, `mrc`, `mrcx`, `ma`, `nb`, `mb`, `mathml`, `mbox`, `mscml`, `metalink`, `meta4`, `mets`, `maei`, `musd`, `mods`, `m21`, `mp21`, `mp4s`, `m4p`, `doc`, `dot`, `mxf`, `nq`, `nt`, `cjs`, `bin`, `dms`, `lrf`, `mar`, `so`, `dist`, `distz`, `pkg`, `bpk`, `dump`, `elc`, `deploy`, `exe`, `dll`, `deb`, `dmg`, `iso`, `img`, `msi`, `msp`, `msm`, `buffer`, `oda`, `opf`, `ogx`, `omdoc`, `onetoc`, `onetoc2`, `onetmp`, `onepkg`, `oxps`, `relo`, `xer`, `pdf`, `pgp`, `asc`, `sig`, `prf`, `p10`, `p7m`, `p7c`, `p7s`, `p8`, `ac`, `cer`, `crl`, `pkipath`, `pki`, `pls`, `ai`, `eps`, `ps`, `provx`, `pskcxml`, `raml`, `rdf`, `owl`, `rif`, `rnc`, `rl`, `rld`, `rs`, `rapd`, `sls`, `rusd`, `gbr`, `mft`, `roa`, `rsd`, `rss`, `rtf`, `sbml`, `scq`, `scs`, `spq`, `spp`, `sdp`, `senmlx`, `sensmlx`, `setpay`, `setreg`, `shf`, `siv`, `sieve`, `smi`, `smil`, `rq`, `srx`, `gram`, `grxml`, `sru`, `ssdl`, `ssml`, `swidtag`, `tei`, `teicorpus`, `tfi`, `tsd`, `toml`, `trig`, `ttml`, `ubj`, `rsheet`, `td`, `vxml`, `wasm`, `wgt`, `hlp`, `wsdl`, `wspolicy`, `xaml`, `xav`, `xca`, `xdf`, `xel`, `xns`, `xenc`, `xhtml`, `xht`, `xlf`, `xml`, `xsl`, `xsd`, `rng`, `dtd`, `xop`, `xpl`, `*xsl`, `xslt`, `xspf`, `mxml`, `xhvml`, `xvml`, `xvm`, `yang`, `yin`, `zip`, `*3gpp`, `adp`, `amr`, `au`, `snd`, `mid`, `midi`, `kar`, `rmi`, `mxmf`, `*mp3`, `m4a`, `mp4a`, `mpga`, `mp2`, `mp2a`, `mp3`, `m2a`, `m3a`, `oga`, `ogg`, `spx`, `opus`, `s3m`, `sil`, `wav`, `*wav`, `weba`, `xm`, `ttc`, `otf`, `ttf`, `woff`, `woff2`, `exr`, `apng`, `avif`, `bmp`, `cgm`, `drle`, `emf`, `fits`, `g3`, `gif`, `heic`, `heics`, `heif`, `heifs`, `hej2`, `hsj2`, `ief`, `jls`, `jp2`, `jpg2`, `jpeg`, `jpg`, `jpe`, `jph`, `jhc`, `jpm`, `jpx`, `jpf`, `jxr`, `jxra`, `jxrs`, `jxs`, `jxsc`, `jxsi`, `jxss`, `ktx`, `ktx2`, `png`, `sgi`, `svg`, `svgz`, `t38`, `tif`, `tiff`, `tfx`, `webp`, `wmf`, `disposition-notification`, `u8msg`, `u8dsn`, `u8mdn`, `u8hdr`, `eml`, `mime`, `3mf`, `gltf`, `glb`, `igs`, `iges`, `msh`, `mesh`, `silo`, `mtl`, `obj`, `stpx`, `stpz`, `stpxz`, `stl`, `wrl`, `vrml`, `*x3db`, `x3dbz`, `x3db`, `*x3dv`, `x3dvz`, `x3d`, `x3dz`, `x3dv`, `appcache`, `manifest`, `ics`, `ifb`, `coffee`, `litcoffee`, `css`, `csv`, `html`, `htm`, `shtml`, `jade`, `jsx`, `less`, `markdown`, `md`, `mml`, `mdx`, `n3`, `txt`, `text`, `conf`, `def`, `list`, `log`, `in`, `ini`, `rtx`, `*rtf`, `sgml`, `sgm`, `shex`, `slim`, `slm`, `spdx`, `stylus`, `styl`, `tsv`, `t`, `tr`, `roff`, `man`, `me`, `ms`, `ttl`, `uri`, `uris`, `urls`, `vcard`, `vtt`, `*xml`, `yaml`, `yml`, `3gp`, `3gpp`, `3g2`, `h261`, `h263`, `h264`, `m4s`, `jpgv`, `*jpm`, `jpgm`, `mj2`, `mjp2`, `ts`, `mp4`, `mp4v`, `mpg4`, `mpeg`, `mpg`, `mpe`, `m1v`, `m2v`, `ogv`, `qt`, `mov`, `webm`, `cww`, `1km`, `plb`, `psb`, `pvb`, `tcap`, `pwn`, `aso`, `imp`, `acu`, `atc`, `acutc`, `air`, `fcdt`, `fxp`, `fxpl`, `xdp`, `xfdf`, `ahead`, `azf`, `azs`, `azw`, `acc`, `ami`, `apk`, `cii`, `fti`, `atx`, `mpkg`, `key`, `m3u8`, `numbers`, `pages`, `pkpass`, `swi`, `iota`, `aep`, `bmml`, `mpm`, `bmi`, `rep`, `cdxml`, `mmd`, `cdy`, `csl`, `cla`, `rp9`, `c4g`, `c4d`, `c4f`, `c4p`, `c4u`, `c11amc`, `c11amz`, `csp`, `cdbcmsg`, `cmc`, `clkx`, `clkk`, `clkp`, `clkt`, `clkw`, `wbs`, `pml`, `ppd`, `car`, `pcurl`, `dart`, `rdz`, `dbf`, `uvf`, `uvvf`, `uvd`, `uvvd`, `uvt`, `uvvt`, `uvx`, `uvvx`, `uvz`, `uvvz`, `fe_launch`, `dna`, `mlp`, `mle`, `dpg`, `dfac`, `kpxx`, `ait`, `svc`, `geo`, `mag`, `nml`, `esf`, `msf`, `qam`, `slt`, `ssf`, `es3`, `et3`, `ez2`, `ez3`, `fdf`, `mseed`, `seed`, `dataless`, `gph`, `ftc`, `fm`, `frame`, `maker`, `book`, `fnc`, `ltf`, `fsc`, `oas`, `oa2`, `oa3`, `fg5`, `bh2`, `ddd`, `xdw`, `xbd`, `fzs`, `txd`, `ggb`, `ggt`, `gex`, `gre`, `gxt`, `g2w`, `g3w`, `gmx`, `gdoc`, `gslides`, `gsheet`, `kml`, `kmz`, `gqf`, `gqs`, `gac`, `ghf`, `gim`, `grv`, `gtm`, `tpl`, `vcg`, `hal`, `zmm`, `hbci`, `les`, `hpgl`, `hpid`, `hps`, `jlt`, `pcl`, `pclxl`, `sfd-hdstx`, `mpy`, `afp`, `listafp`, `list3820`, `irm`, `sc`, `icc`, `icm`, `igl`, `ivp`, `ivu`, `igm`, `xpw`, `xpx`, `i2g`, `qbo`, `qfx`, `rcprofile`, `irp`, `xpr`, `fcs`, `jam`, `rms`, `jisp`, `joda`, `ktz`, `ktr`, `karbon`, `chrt`, `kfo`, `flw`, `kon`, `kpr`, `kpt`, `ksp`, `kwd`, `kwt`, `htke`, `kia`, `kne`, `knp`, `skp`, `skd`, `skt`, `skm`, `sse`, `lasxml`, `lbd`, `lbe`, `apr`, `pre`, `nsf`, `org`, `scm`, `lwp`, `portpkg`, `mvt`, `mcd`, `mc1`, `cdkey`, `mwf`, `mfm`, `flo`, `igx`, `mif`, `daf`, `dis`, `mbk`, `mqy`, `msl`, `plc`, `txf`, `mpn`, `mpc`, `xul`, `cil`, `cab`, `xls`, `xlm`, `xla`, `xlc`, `xlt`, `xlw`, `xlam`, `xlsb`, `xlsm`, `xltm`, `eot`, `chm`, `ims`, `lrm`, `thmx`, `msg`, `cat`, `*stl`, `ppt`, `pps`, `pot`, `ppam`, `pptm`, `sldm`, `ppsm`, `potm`, `mpp`, `mpt`, `docm`, `dotm`, `wps`, `wks`, `wcm`, `wdb`, `wpl`, `xps`, `mseq`, `mus`, `msty`, `taglet`, `nlu`, `ntf`, `nitf`, `nnd`, `nns`, `nnw`, `*ac`, `ngdat`, `n-gage`, `rpst`, `rpss`, `edm`, `edx`, `ext`, `odc`, `otc`, `odb`, `odf`, `odft`, `odg`, `otg`, `odi`, `oti`, `odp`, `otp`, `ods`, `ots`, `odt`, `odm`, `ott`, `oth`, `xo`, `dd2`, `obgx`, `oxt`, `osm`, `pptx`, `sldx`, `ppsx`, `potx`, `xlsx`, `xltx`, `docx`, `dotx`, `mgp`, `dp`, `esa`, `pdb`, `pqa`, `oprc`, `paw`, `str`, `ei6`, `efif`, `wg`, `plf`, `pbd`, `box`, `mgz`, `qps`, `ptid`, `qxd`, `qxt`, `qwd`, `qwt`, `qxl`, `qxb`, `rar`, `bed`, `mxl`, `musicxml`, `cryptonote`, `cod`, `rm`, `rmvb`, `link66`, `st`, `see`, `sema`, `semd`, `semf`, `ifm`, `itp`, `iif`, `ipk`, `twd`, `twds`, `mmf`, `teacher`, `fo`, `sdkm`, `sdkd`, `dxp`, `sfs`, `sdc`, `sda`, `sdd`, `smf`, `sdw`, `vor`, `sgl`, `smzip`, `sm`, `wadl`, `sxc`, `stc`, `sxd`, `std`, `sxi`, `sti`, `sxm`, `sxw`, `sxg`, `stw`, `sus`, `susp`, `svd`, `sis`, `sisx`, `xsm`, `bdm`, `xdm`, `ddf`, `tao`, `pcap`, `cap`, `dmp`, `tmo`, `tpt`, `mxs`, `tra`, `ufd`, `ufdl`, `utz`, `umj`, `unityweb`, `uoml`, `vcx`, `vsd`, `vst`, `vss`, `vsw`, `vis`, `vsf`, `wbxml`, `wmlc`, `wmlsc`, `wtb`, `nbp`, `wpd`, `wqd`, `stf`, `xar`, `xfdl`, `hvd`, `hvs`, `hvp`, `osf`, `osfpvg`, `saf`, `spf`, `cmp`, `zir`, `zirz`, `zaz`, `7z`, `abw`, `ace`, `*dmg`, `arj`, `aab`, `x32`, `u32`, `vox`, `aam`, `aas`, `bcpio`, `*bdoc`, `torrent`, `blb`, `blorb`, `bz`, `bz2`, `boz`, `cbr`, `cba`, `cbt`, `cbz`, `cb7`, `vcd`, `cfs`, `chat`, `pgn`, `crx`, `cco`, `nsc`, `cpio`, `csh`, `*deb`, `udeb`, `dgc`, `dir`, `dcr`, `dxr`, `cst`, `cct`, `cxt`, `w3d`, `fgd`, `swa`, `wad`, `ncx`, `dtb`, `res`, `dvi`, `evy`, `eva`, `bdf`, `gsf`, `psf`, `pcf`, `snf`, `pfa`, `pfb`, `pfm`, `afm`, `arc`, `spl`, `gca`, `ulx`, `gnumeric`, `gramps`, `gtar`, `hdf`, `php`, `install`, `*iso`, `*key`, `*numbers`, `*pages`, `jardiff`, `jnlp`, `kdbx`, `latex`, `luac`, `lzh`, `lha`, `run`, `mie`, `prc`, `mobi`, `application`, `lnk`, `wmd`, `wmz`, `xbap`, `mdb`, `obd`, `crd`, `clp`, `*exe`, `*dll`, `com`, `bat`, `*msi`, `mvb`, `m13`, `m14`, `*wmf`, `*wmz`, `*emf`, `emz`, `mny`, `pub`, `scd`, `trm`, `wri`, `nc`, `cdf`, `pac`, `nzb`, `pl`, `pm`, `*prc`, `*pdb`, `p12`, `pfx`, `p7b`, `spc`, `p7r`, `*rar`, `rpm`, `ris`, `sea`, `sh`, `shar`, `swf`, `xap`, `sql`, `sit`, `sitx`, `srt`, `sv4cpio`, `sv4crc`, `t3`, `gam`, `tar`, `tcl`, `tk`, `tex`, `tfm`, `texinfo`, `texi`, `*obj`, `ustar`, `hdd`, `ova`, `ovf`, `vbox`, `vbox-extpack`, `vdi`, `vhd`, `vmdk`, `src`, `webapp`, `der`, `crt`, `pem`, `fig`, `*xlf`, `xpi`, `xz`, `z1`, `z2`, `z3`, `z4`, `z5`, `z6`, `z7`, `z8`, `uva`, `uvva`, `eol`, `dra`, `dts`, `dtshd`, `lvp`, `pya`, `ecelp4800`, `ecelp7470`, `ecelp9600`, `rip`, `aac`, `aif`, `aiff`, `aifc`, `caf`, `flac`, `*m4a`, `mka`, `m3u`, `wax`, `wma`, `ram`, `ra`, `rmp`, `*ra`, `cdx`, `cif`, `cmdf`, `cml`, `csml`, `xyz`, `btif`, `pti`, `psd`, `azv`, `uvi`, `uvvi`, `uvg`, `uvvg`, `djvu`, `djv`, `*sub`, `dwg`, `dxf`, `fbs`, `fpx`, `fst`, `mmr`, `rlc`, `ico`, `dds`, `mdi`, `wdp`, `npx`, `b16`, `tap`, `vtf`, `wbmp`, `xif`, `pcx`, `3ds`, `ras`, `cmx`, `fh`, `fhc`, `fh4`, `fh5`, `fh7`, `*ico`, `jng`, `sid`, `*bmp`, `*pcx`, `pic`, `pct`, `pnm`, `pbm`, `pgm`, `ppm`, `rgb`, `tga`, `xbm`, `xpm`, `xwd`, `wsc`, `dae`, `dwf`, `gdl`, `gtw`, `mts`, `ogex`, `x_b`, `x_t`, `vds`, `usdz`, `bsp`, `vtu`, `dsc`, `curl`, `dcurl`, `mcurl`, `scurl`, `sub`, `fly`, `flx`, `gv`, `3dml`, `spot`, `jad`, `wml`, `wmls`, `s`, `asm`, `c`, `cc`, `cxx`, `cpp`, `h`, `hh`, `dic`, `htc`, `f`, `for`, `f77`, `f90`, `hbs`, `java`, `lua`, `mkd`, `nfo`, `opml`, `*org`, `p`, `pas`, `pde`, `sass`, `scss`, `etx`, `sfv`, `ymp`, `uu`, `vcs`, `vcf`, `uvh`, `uvvh`, `uvm`, `uvvm`, `uvp`, `uvvp`, `uvs`, `uvvs`, `uvv`, `uvvv`, `dvb`, `fvt`, `mxu`, `m4u`, `pyv`, `uvu`, `uvvu`, `viv`, `f4v`, `fli`, `flv`, `m4v`, `mkv`, `mk3d`, `mks`, `mng`, `asf`, `asx`, `vob`, `wm`, `wmv`, `wmx`, `wvx`, `avi`, `movie`, `smv`, `ice`, `mht` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/hris.md b/.claude/plugins/stackone-connector-builder/references/schemas/hris.md new file mode 100644 index 0000000..97956d0 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/hris.md @@ -0,0 +1,847 @@ +# HRIS (Human Resources Information System) — Schema Reference + +> Manage employees, departments, locations, time off, payroll, benefits and more. + +> **OAS source:** `https://docs.stackone.com/hris/api-reference/hris.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/hris/companies` | List Companies | +| `GET` | `/unified/hris/companies/{id}` | Get Company | +| `GET` | `/unified/hris/custom_field_definitions/employees` | List employee Custom Field Definitions | +| `GET` | `/unified/hris/custom_field_definitions/employees/{id}` | Get employee Custom Field Definition | +| `GET` | `/unified/hris/employees` | List Employees | +| `POST` | `/unified/hris/employees` | Create Employee | +| `GET` | `/unified/hris/employees/{id}` | Get Employee | +| `PATCH` | `/unified/hris/employees/{id}` | Update Employee | +| `POST` | `/unified/hris/employees/{id}/invite` | Invite Employee | +| `GET` | `/unified/hris/employees/{id}/shifts` | List Employee Shifts | +| `GET` | `/unified/hris/employees/{id}/shifts/{subResourceId}` | Get Employee Shift | +| `GET` | `/unified/hris/employees/{id}/time_off` | List Employee Time Off Requests | +| `POST` | `/unified/hris/employees/{id}/time_off` | Create Employee Time Off Request | +| `GET` | `/unified/hris/employees/{id}/time_off/{subResourceId}` | Get Employees Time Off Request | +| `PATCH` | `/unified/hris/employees/{id}/time_off/{subResourceId}` | Update Employee Time Off Request | +| `DELETE` | `/unified/hris/employees/{id}/time_off/{subResourceId}` | Cancel Employee Time Off Request | +| `POST` | `/unified/hris/employees/{id}/documents/upload/batch` | Batch Upload Employee Document | +| `POST` | `/unified/hris/employees/{id}/documents/upload` | Upload Employee Document | +| `GET` | `/unified/hris/employees/{id}/documents/{subResourceId}/download` | Download Employee Document | +| `GET` | `/unified/hris/employees/{id}/documents` | List Employee Documents | +| `GET` | `/unified/hris/employees/{id}/documents/{subResourceId}` | Get Employee Document | +| `GET` | `/unified/hris/documents/employee_categories` | List Employee Document Categories | +| `GET` | `/unified/hris/documents/employee_categories/{id}` | Get Employee Document Category | +| `GET` | `/unified/hris/employees/{id}/work_eligibility` | List Employee Work Eligibility | +| `POST` | `/unified/hris/employees/{id}/work_eligibility` | Create Employee Work Eligibility Request | +| `GET` | `/unified/hris/employees/{id}/work_eligibility/{subResourceId}` | Get Employees Work Eligibility | +| `PATCH` | `/unified/hris/employees/{id}/work_eligibility/{subResourceId}` | Update Employee Work Eligibility Request | +| `GET` | `/unified/hris/employees/{id}/time_off_balances` | List Employee Time Off Balances | +| `GET` | `/unified/hris/employees/{id}/time_off_balances/{subResourceId}` | Get Employee Time Off Balance | +| `GET` | `/unified/hris/employments` | List Employments | +| `GET` | `/unified/hris/employments/{id}` | Get Employment | +| `GET` | `/unified/hris/employees/{id}/employments` | List Employee Employments | +| `POST` | `/unified/hris/employees/{id}/employments` | Create Employee Employment | +| `GET` | `/unified/hris/employees/{id}/employments/{subResourceId}` | Get Employee Employment | +| `PATCH` | `/unified/hris/employees/{id}/employments/{subResourceId}` | Update Employee Employment | +| `GET` | `/unified/hris/groups` | List Groups | +| `GET` | `/unified/hris/groups/departments` | List Department Groups | +| `GET` | `/unified/hris/groups/cost_centers` | List Cost Center Groups | +| `GET` | `/unified/hris/groups/teams` | List Team Groups | +| `GET` | `/unified/hris/groups/divisions` | List Division Groups | +| `GET` | `/unified/hris/groups/companies` | List Companies Groups | +| `GET` | `/unified/hris/groups/{id}` | Get Group | +| `GET` | `/unified/hris/groups/departments/{id}` | Get Department Group | +| `GET` | `/unified/hris/groups/cost_centers/{id}` | Get Cost Center Group | +| `GET` | `/unified/hris/groups/teams/{id}` | Get Team Group | +| `GET` | `/unified/hris/groups/divisions/{id}` | Get Division Group | +| `GET` | `/unified/hris/groups/companies/{id}` | Get Company Group | +| `GET` | `/unified/hris/jobs` | List Jobs | +| `GET` | `/unified/hris/jobs/{id}` | Get Job | +| `GET` | `/unified/hris/locations` | List Work Locations | +| `GET` | `/unified/hris/locations/{id}` | Get Work Location | +| `GET` | `/unified/hris/positions` | List Positions | +| `GET` | `/unified/hris/positions/{id}` | Get Position | +| `GET` | `/unified/hris/time_entries` | List Time Entries | +| `GET` | `/unified/hris/time_entries/{id}` | Get Time Entry | +| `GET` | `/unified/hris/time_off` | List time off requests | +| `GET` | `/unified/hris/time_off/{id}` | Get time off request | +| `GET` | `/unified/hris/shifts` | List Shifts | +| `GET` | `/unified/hris/shifts/{id}` | Get Shift | +| `GET` | `/unified/hris/time_off_types` | List time off types | +| `GET` | `/unified/hris/time_off_types/{id}` | Get time off type | +| `GET` | `/unified/hris/time_off_policies` | List Time Off Policies | +| `GET` | `/unified/hris/time_off_policies/{id}` | Get Time Off Policy | +| `GET` | `/unified/hris/employees/{id}/time_off_policies` | List Assigned Time Off Policies | +| `GET` | `/unified/hris/benefits` | List benefits | +| `GET` | `/unified/hris/benefits/{id}` | Get Benefit | +| `GET` | `/unified/hris/employees/{id}/skills` | List Employee Skills | +| `POST` | `/unified/hris/employees/{id}/skills` | Create Employee Skill | +| `GET` | `/unified/hris/employees/{id}/skills/{subResourceId}` | Get Employee Skill | +| `GET` | `/unified/hris/employees/{id}/tasks` | List Employee Tasks | +| `GET` | `/unified/hris/employees/{id}/tasks/{subResourceId}` | Get Employee Task | +| `PATCH` | `/unified/hris/employees/{id}/tasks/{subResourceId}` | Update Employee Task | +| `GET` | `/unified/hris/tasks` | List Tasks | +| `GET` | `/unified/hris/tasks/{id}` | Get Task | + +--- + +## Models + +### `ClearingCode` +| Field | Type | Description | +|-------|------|-------------| +| `type` | `ClearingCodeTypeEnum` | The type of clearing code | +| `value` | `string` | The clearing code value | + +### `Company` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the company | +| `full_name` | `string` | The full name of the company | +| `display_name` | `string` | The display name of the company | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | + +### `Content` +| Field | Type | Description | +|-------|------|-------------| +| `url` | `string` | URL where the file content is located | +| `unified_url` | `string` | Unified download URL for retrieving file content. | +| `file_format` | `FileFormatEnum` | The file format of the file | + +### `ContractTypeApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `label` | `string` | The label of the employment type | +| `contract_type` | `ContractTypeEnum` | The employment work schedule type (e.g., full-time, part-time) | + +### `CostCenters` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `distribution_percentage` | `number` | | + +### `CreateCostCenterApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | | +| `distribution_percentage` | `number` | | + +### `CreateEmployeeEmploymentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `job_title` | `string` | The job title of the employee | +| `pay_rate` | `string` | The amount of compensation for the employee | +| `pay_period` | `PayPeriodEnum` | The time period over which the pay rate is calculated | +| `pay_frequency` | `PayFrequencyEnum` | How often the employee is paid | +| `pay_currency` | `string` | The currency used for pay | +| `end_date` | `string` | The end date of employment | +| `grade` | `EmploymentGradeApiModel` | Represents the employee’s position within the organizational hierarchy. | +| `employment_type` | `EmploymentTypeEnum` | The type of employment (e.g., contractor, permanent) | +| `employment_contract_type` | `EmploymentScheduleTypeEnum` | The employment work schedule type (e.g., full-time, part-time) | +| `type` | `TypeApiModel` | The type of employment | +| `contract_type` | `ContractTypeApiModel` | The employment work schedule type | +| `work_time` | `WorkTimeApiModel` | | +| `payroll_code` | `string` | The payroll code of the employee | +| `passthrough` | `object` | Value to pass through to the provider | +| `effective_date` | `string` | The employee effective date | + +### `CreateEmployeeLocationApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | The name of the location | +| `phone_number` | `string` | The phone number of the location | +| `street_1` | `string` | The first line of the address | +| `street_2` | `string` | The second line of the address | +| `city` | `string` | The city where the location is situated | +| `zip_code` | `string` | The ZIP code/Postal code of the location | +| `country` | `CountryCodeEnum` | The country code | +| `passthrough` | `object` | Value to pass through to the provider | +| `state` | `ISO3166_2SubDivisionEnum` | The ISO3166-2 sub division where the location is situated | + +### `CreateEmploymentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `job_title` | `string` | The job title of the employee | +| `pay_rate` | `string` | The amount of compensation for the employee | +| `pay_period` | `PayPeriodEnum` | The time period over which the pay rate is calculated | +| `pay_frequency` | `PayFrequencyEnum` | How often the employee is paid | +| `pay_currency` | `string` | The currency used for pay | +| `effective_date` | `string` | The effective date of the employment contract | +| `end_date` | `string` | The end date of employment | +| `grade` | `EmploymentGradeApiModel` | Represents the employee’s position within the organizational hierarchy. | +| `employment_type` | `EmploymentTypeEnum` | The type of employment (e.g., contractor, permanent) | +| `employment_contract_type` | `EmploymentScheduleTypeEnum` | The employment work schedule type (e.g., full-time, part-time) | +| `type` | `TypeApiModel` | The type of employment | +| `contract_type` | `ContractTypeApiModel` | The employment work schedule type | +| `work_time` | `WorkTimeApiModel` | | +| `payroll_code` | `string` | The payroll code of the employee | +| `job_id` | `string` | The employee job id | + +### `CreateHRISBenefit` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | The name of the benefit | +| `benefit_type` | `BenefitsTypeEnum` | The type of the benefit | +| `provider` | `string` | The provider of the benefit | +| `description` | `string` | The description of the benefit | +| `created_at` | `string` | The date and time the benefit was created | +| `updated_at` | `string` | The date and time the benefit was last updated | + +### `CustomFieldDefinition` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `description` | `string` | | +| `type` | `CustomFieldTypeEnum` | The type of the custom field. | +| `options` | `array` | An array of possible options for the custom field. | + +### `CustomFieldDefinitionResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `data` | `CustomFieldDefinition` | | +| `raw` | `array` | | + +### `CustomFieldOption` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique identifier for the option to be used when updating the custom field | +| `value` | `object` | The human readable value of the option | + +### `CustomFields` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the custom field. | +| `value` | `object` | The value associated with the custom field. | +| `value_id` | `string` | The unique identifier for the value of the custom field. | +| `remote_value_id` | `string` | Provider's unique identifier for the value of the custom field. | + +### `Employee` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `title` | `string` | The prefix of the employee's name (e.g., Mr, Ms, Dr) | +| `first_name` | `string` | The employee first name | +| `last_name` | `string` | The employee last name | +| `name` | `string` | The employee name | +| `display_name` | `string` | The employee display name | +| `avatar_url` | `string` | The employee avatar Url | +| `personal_email` | `string` | The employee personal email | +| `personal_phone_number` | `string` | The employee personal phone number | +| `work_email` | `string` | The employee work email | +| `work_phone_number` | `string` | The employee work phone number | +| `job_id` | `string` | The employee job id | +| `job_title` | `string` | The employee job title | +| `job_description` | `JobDescriptionApiModel` | The employee job description | +| `department_id` | `string` | The employee department id | +| `department` | `string` | The employee department | +| `groups` | `array` | The employee groups | +| `cost_centers` | `array` | The employee cost centers | +| `manager_id` | `string` | The employee manager ID | +| `remote_manager_id` | `string` | Provider's unique identifier of the manager | +| `gender` | `GenderEnum` | The employee gender | +| `preferred_language` | `PreferredLanguageEnum` | The employee preferred language | +| `ethnicity` | `EthnicityEnum` | The employee ethnicity | +| `date_of_birth` | `string` | The date when the employee was born | +| `birthday` | `string` | The next birthday of the employee (upcoming birthday) | +| `marital_status` | `MaritalStatusEnum` | The employee marital status | +| `avatar` | `Image` | The employee avatar | +| `hire_date` | `string` | The employee hire date | +| `start_date` | `string` | The employee start date | +| `tenure` | `number` | The employee tenure | +| `work_anniversary` | `string` | The employee work anniversary | +| `employment_type` | `EmploymentTypeEnum` | The employee employment type | +| `employment_contract_type` | `EmploymentScheduleTypeEnum` | The employment work schedule type (e.g., full-time, part-time) | +| `employment_status` | `EmploymentStatusEnum` | The employee employment status | +| `termination_date` | `string` | The employee termination date | +| `company_name` | `string` | The employee company name | +| `company_id` | `string` | The employee company id | +| `citizenships` | `array` | The citizenships of the Employee | +| `home_location` | `HRISLocation` | The employee home location | +| `work_location` | `HRISLocation` | The employee work location | +| `company` | `Company` | The employee company | +| `employments` | `array` | The employee employments | +| `custom_fields` | `array` | The employee custom fields | +| `benefits` | `array` | Current benefits of the employee | +| `employee_number` | `string` | The assigned employee number | +| `bank_details` | `array` | Bank account details for the employee | +| `national_identity_number` | `NationalIdentityNumberApiModel` | The national identity number | +| `national_identity_numbers` | `array` | The national identity numbers | +| `skills` | `array` | The employee skills | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | + +### `Employment` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `job_title` | `string` | The job title of the employee | +| `pay_rate` | `string` | The amount of compensation for the employee | +| `pay_period` | `PayPeriodEnum` | The time period over which the pay rate is calculated | +| `pay_frequency` | `PayFrequencyEnum` | How often the employee is paid | +| `pay_currency` | `string` | The currency used for pay | +| `effective_date` | `string` | The effective date of the employment contract | +| `end_date` | `string` | The end date of employment | +| `grade` | `EmploymentGradeApiModel` | Represents the employee’s position within the organizational hierarchy. | +| `employment_type` | `EmploymentTypeEnum` | The type of employment (e.g., contractor, permanent) | +| `employment_contract_type` | `EmploymentScheduleTypeEnum` | The employment work schedule type (e.g., full-time, part-time) | +| `type` | `TypeApiModel` | The type of employment | +| `contract_type` | `ContractTypeApiModel` | The employment work schedule type | +| `work_time` | `WorkTimeApiModel` | | +| `payroll_code` | `string` | The payroll code of the employee | +| `employee_id` | `string` | The employee ID associated with this employment | +| `remote_employee_id` | `string` | Provider's unique identifier of the employee associated with this employment | +| `fte` | `number` | the employee's working percentage relative to a full-time employee | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | +| `start_date` | `string` | The start_date of employment | +| `active` | `object` | The employment active status | +| `department` | `HRISGroup` | The employee department | +| `cost_center` | `HRISGroup` | The employee cost_center | +| `cost_centers` | `array` | The employee cost_centers | +| `division` | `HRISGroup` | The employee division | +| `job` | `EmploymentJobApiModel` | The job of employee | +| `manager` | `array` | The employee manager | + +### `EmploymentGradeApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The reference id | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The reference name | +| `description` | `string` | description of the grade | + +### `EmploymentJobApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `title` | `string` | Title of the job | +| `description` | `JobDescriptionApiModel` | The employee job description | +| `owner_id` | `string` | The owner_id of the job | +| `parent_id` | `string` | The parent_id of the job | + +### `EmploymentManagerApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The manager name | +| `email` | `string` | The manager email | +| `role` | `ManagerRoleApiModel` | The role of manager | + +### `EntitySkills` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this skill | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name associated with this skill | +| `active` | `object` | Whether the skill is active and therefore available for use | +| `language` | `LanguageEnum` | The language associated with this skill | +| `maximum_proficiency` | `Proficiency` | The proficiency level of the skill | +| `minimum_proficiency` | `Proficiency` | The proficiency level of the skill | + +### `File` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the file | +| `category` | `FileCategoryEnumApiModel` | The category of the file | +| `contents` | `array` | The content of the file. Deprecated, use `url` and `file_format` one level up instead | +| `category_id` | `string` | The categoryId of the documents | +| `created_at` | `string` | The creation date of the file | +| `updated_at` | `string` | The update date of the file | +| `remote_url` | `string` | URL where the file content is located | +| `file_format` | `FileFormatEnum` | The file format of the file | + +### `FileCategoryEnumApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | The category of the file | +| `source_value` | `object` | | + +### `HRISBankDetails` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `account_name` | `string` | The name of the bank account | +| `is_primary` | `object` | Whether this is the primary bank account | +| `country_code` | `CountryCodeEnum` | The country code where the bank is located | +| `currency_code` | `CurrencyCodeEnum` | The currency code for the account | +| `bank_name` | `string` | The name of the bank | +| `account_type` | `BankAccountTypeEnum` | The type of bank account | +| `iban` | `string` | International Bank Account Number (IBAN) | +| `local_account_number` | `string` | Local account number (used when IBAN is not available) | +| `swift_bic` | `string` | SWIFT/BIC code for international transfers | +| `clearing_codes` | `array` | Array of clearing codes required by the country | + +### `HRISBenefit` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the benefit | +| `benefit_type` | `BenefitsTypeEnum` | The type of the benefit | +| `provider` | `string` | The provider of the benefit | +| `description` | `string` | The description of the benefit | +| `created_at` | `string` | The date and time the benefit was created | +| `updated_at` | `string` | The date and time the benefit was last updated | + +### `HRISCostCenter` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the group | +| `parent_ids` | `array` | The list of parent group ids of the given group | +| `remote_parent_ids` | `array` | Provider's list of parent group remote ids of the given group | +| `owner_ids` | `array` | The list of group owner ids of the given group | +| `remote_owner_ids` | `array` | The list of remote group owner ids of the given group | +| `company_id` | `string` | The id of the company that the group belongs to | +| `remote_company_id` | `string` | Provider's id of the company that the group belongs to | +| `distribution_percentage` | `number` | The distribution percentage for cost_center | +| `type` | `GroupTypeEnum` | The type of the group | + +### `HRISDepartment` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the group | +| `parent_ids` | `array` | The list of parent group ids of the given group | +| `remote_parent_ids` | `array` | Provider's list of parent group remote ids of the given group | +| `owner_ids` | `array` | The list of group owner ids of the given group | +| `remote_owner_ids` | `array` | The list of remote group owner ids of the given group | +| `company_id` | `string` | The id of the company that the group belongs to | +| `remote_company_id` | `string` | Provider's id of the company that the group belongs to | +| `type` | `DepartmentTypeEnum` | The type of the department group | + +### `HRISDivision` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the group | +| `parent_ids` | `array` | The list of parent group ids of the given group | +| `remote_parent_ids` | `array` | Provider's list of parent group remote ids of the given group | +| `owner_ids` | `array` | The list of group owner ids of the given group | +| `remote_owner_ids` | `array` | The list of remote group owner ids of the given group | +| `company_id` | `string` | The id of the company that the group belongs to | +| `remote_company_id` | `string` | Provider's id of the company that the group belongs to | +| `type` | `DivisionTypeEnum` | The type of the division group | + +### `HRISGroup` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the group | +| `parent_ids` | `array` | The list of parent group ids of the given group | +| `remote_parent_ids` | `array` | Provider's list of parent group remote ids of the given group | +| `owner_ids` | `array` | The list of group owner ids of the given group | +| `remote_owner_ids` | `array` | The list of remote group owner ids of the given group | +| `company_id` | `string` | The id of the company that the group belongs to | +| `remote_company_id` | `string` | Provider's id of the company that the group belongs to | +| `type` | `GroupTypeEnum` | The type of the group | + +### `HRISLocation` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `employee_id` | `string` | The employee ID | +| `remote_employee_id` | `string` | Provider's unique identifier of the employee | +| `name` | `string` | The name of the location | +| `phone_number` | `string` | The phone number of the location | +| `street_1` | `string` | The first line of the address | +| `street_2` | `string` | The second line of the address | +| `city` | `string` | The city where the location is situated | +| `state` | `string` | The state where the location is situated | +| `zip_code` | `string` | The ZIP code/Postal code of the location | +| `country` | `CountryCodeEnum` | The country code | +| `location_type` | `LocationTypeEnum` | The location type | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | + +### `HRISTeam` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name of the group | +| `parent_ids` | `array` | The list of parent group ids of the given group | +| `remote_parent_ids` | `array` | Provider's list of parent group remote ids of the given group | +| `owner_ids` | `array` | The list of group owner ids of the given group | +| `remote_owner_ids` | `array` | The list of remote group owner ids of the given group | +| `company_id` | `string` | The id of the company that the group belongs to | +| `remote_company_id` | `string` | Provider's id of the company that the group belongs to | +| `type` | `TeamTypeEnum` | The type of the team group | + +### `HrisDocumentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the file | +| `category` | `HrisDocumentTypeEnum` | The category of the the document | +| `contents` | `array` | The content of the file. Deprecated, use `url` and `file_format` one level up instead | +| `category_id` | `string` | The categoryId of the documents | +| `created_at` | `string` | The creation date of the file | +| `updated_at` | `string` | The update date of the file | +| `remote_url` | `string` | URL where the file content is located | +| `file_format` | `FileFormatEnum` | The file format of the file | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `type` | `HrisDocumentTypeEnum` | The content type of the document | + +### `HrisJob` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `code` | `string` | Code of the job | +| `title` | `string` | Title of the job | +| `description` | `string` | Description of the job | +| `status` | `HrisJobStatusEnum` | Status of the job | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `HrisShift` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `employee_id` | `string` | The employee ID associated with this shift | +| `location_id` | `string` | The location ID where this shift takes place | +| `company_id` | `string` | The company ID associated with this shift | +| `start_time` | `string` | The start time of the shift (ISO8601 date-time without timezone) | +| `end_time` | `string` | The end time of the shift (ISO8601 date-time without timezone) | +| `break_duration` | `string` | The total break duration for this shift in ISO 8601 duration format | +| `status` | `ShiftStatusEnum` | The status of the shift | +| `approval_status` | `ShiftApprovalStatusEnum` | The approval status of the shift | +| `breaks` | `array` | The breaks taken during this shift | +| `created_at` | `string` | The date and time the shift was created | +| `updated_at` | `string` | The date and time the shift was last updated | + +### `ISO3166_2SubDivisionEnum` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | state (ISO3166-2 Sub Division Code) - value must be a valid enum value | +| `source_value` | `object` | | + +### `Image` +| Field | Type | Description | +|-------|------|-------------| +| `url` | `string` | | +| `base64` | `string` | | + +### `JobDescriptionApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `text` | `string` | | + +### `LaborTypeApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `code` | `string` | | + +### `ManagerRoleApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `label` | `string` | The label of the role type | +| `role_type` | `RoleTypeEnum` | The manager role type (e.g., admin, viewer) | + +### `NationalIdentityNumberApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | | +| `type` | `NationalIdentityNumberTypeEnumApiModel` | | +| `country` | `CountryCodeEnum` | The country code | + +### `Position` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `job_id` | `string` | Job ID from the HRIS provider | +| `title` | `string` | Title of the position | +| `description` | `string` | Description of the position | +| `department_id` | `string` | Department ID associated with the position | +| `location_id` | `string` | Location ID associated with the position | +| `employment_type` | `string` | Employment type for the position | +| `status` | `PositionStatusEnum` | Status of the position | +| `posted_date` | `string` | Date when the position was posted | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `ProviderErrorApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | | +| `url` | `string` | | +| `raw` | `object` | | +| `headers` | `object` | | + +### `Reason` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | + +### `Reference` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The reference id | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The reference name | +| `active` | `object` | The reference status | + +### `ShiftBreak` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique identifier of the break | +| `start_time` | `string` | The start time of the break | +| `end_time` | `string` | The end time of the break | +| `duration` | `string` | The duration of the break in ISO 8601 duration format | +| `is_paid` | `object` | Whether the break is paid | +| `created_at` | `string` | The date and time the break was created | +| `updated_at` | `string` | The date and time the break was last updated | + +### `Task` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `employee_id` | `string` | The employee ID associated with this task | +| `name` | `string` | The name of the task | +| `description` | `string` | The description of the task | +| `type` | `TaskTypeEnum` | The type of the task | +| `status` | `TaskStatusEnum` | The status of the task | +| `due_date` | `string` | The due date of the task | +| `completion_date` | `string` | The completion date of the task | +| `assigned_by_employee_id` | `string` | The ID of the employee who assigned this task | +| `assigned_by_employee_name` | `string` | The name of the employee who assigned this task | +| `link_to_task` | `string` | Link to the task in the provider system | +| `extracted_links` | `array` | List of extracted links from the task | +| `next_task_id` | `string` | ID of the next task in sequence | +| `parent_process_name` | `string` | Name of the parent process of this task | +| `comments` | `array` | The comments associated with this task | +| `attachments` | `array` | The documents attached to this task | +| `created_at` | `string` | The creation date of this task | +| `updated_at` | `string` | The last updated date of this task | + +### `TaskCommentApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `author_employee_id` | `string` | The Employee ID of the author of the comment | +| `comment` | `string` | The text of the comment | +| `created_at` | `string` | The creation date of this comment | + +### `TimeEntries` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `employee_id` | `string` | The employee ID associated with this time entry | +| `remote_employee_id` | `string` | Provider's unique identifier of the employee associated with this time entry | +| `start_time` | `string` | The start time of the time entry | +| `end_time` | `string` | The end time of the time entry | +| `hours_worked` | `number` | The hours worked in the time entry | +| `break_duration` | `number` | The duration of the break taken during time entry in hours | +| `status` | `TimeEntryStatusEnum` | The status of the time entry | +| `labor_type` | `LaborTypeApiModel` | The labor type associated with this time entry | +| `location` | `Reference` | The location of the time entry | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | + +### `TimeOff` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `employee_id` | `string` | The employee ID | +| `remote_employee_id` | `string` | Provider's unique identifier of the employee | +| `approver_id` | `string` | The approver ID | +| `remote_approver_id` | `string` | Provider's unique identifier of the approver | +| `status` | `TimeOffStatusEnum` | The status of the time off request | +| `type` | `TimeOffTypeEnum` | The type of the time off request | +| `start_date` | `string` | The start date of the time off request (ISO8601 date-time without timezone) | +| `end_date` | `string` | Inclusive end date of the time off request (ISO8601 date-time without timezone). The time off inc... | +| `start_half_day` | `object` | True if the start of the time off request begins half way through the day | +| `end_half_day` | `object` | True if the end of the time off request ends half way through the day | +| `duration` | `string` | The duration of the time off request | +| `time_off_policy_id` | `string` | The time off policy id associated with this time off request | +| `remote_time_off_policy_id` | `string` | Provider's unique identifier of the time off policy id associated with this time off request | +| `reason` | `Reason` | | +| `created_at` | `string` | Timestamp when the time off request was created | +| `updated_at` | `string` | Timestamp when the time off request was last updated | +| `policy` | `TimeOffPolicies` | The time off policy associated with Time Off | +| `comment` | `string` | Allows users to provide additional context or notes for their time off request | + +### `TimeOffBalances` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `employee_id` | `string` | The employee id associated with this balance | +| `remote_employee_id` | `string` | Provider's unique identifier of the employee associated with this balance | +| `policy_id` | `string` | The time off policy id associated with this balance | +| `remote_policy_id` | `string` | Provider's unique identifier of the time off policy id associated with this balance | +| `policy` | `TimeOffPolicies` | The time off policy associated with this balance | +| `current_balance` | `number` | The current numeric balance for the associated employee and time off policy | +| `initial_balance` | `number` | The initial numeric balance for the associated employee and time off policy as of the balance sta... | +| `balance_unit` | `TimeOffBalanceUnitEnum` | The duration unit of the current balance | +| `balance_start_date` | `string` | The date of when the initial balance quantity was set | +| `balance_expiry_date` | `string` | The date of when the current balance expires | +| `is_unlimited` | `object` | Indicates if this time off balance represents unlimited leave | +| `updated_at` | `string` | The updated_at date of this time off balance | + +### `TimeOffPolicies` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of this policy | +| `description` | `string` | The description of this policy | +| `type` | `TimeOffPolicyTypeEnum` | The type of this policy | +| `duration_unit` | `TimeOffBalanceUnitEnum` | The duration unit of the current policy | +| `reasons` | `array` | | +| `created_at` | `string` | The created_at date of this policy | +| `updated_at` | `string` | The updated_at date of this policy | + +### `TypeApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `label` | `string` | The label of the employment type | +| `type` | `TypeEnum` | The type of employment (e.g., contractor, permanent) | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +### `UnifiedWarningApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `message` | `string` | | + +### `WorkEligibility` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `type` | `WorkEligibilityTypeEnum` | | +| `sub_type` | `string` | | +| `document` | `File` | | +| `valid_from` | `string` | | +| `valid_to` | `string` | | +| `issued_by` | `CountryCodeEnum` | The country code of the issued by authority | +| `number` | `string` | | + +### `WorkTimeApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `duration` | `string` | The work time duration in ISO 8601 duration format | +| `period` | `WorkTimeUnitEnum` | The period of the work time | + +### `WriteResultApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | | +| `message` | `string` | | +| `timestamp` | `string` | | +| `provider_errors` | `array` | | +| `unified_warnings` | `array` | | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `BankAccountTypeEnum` | `savings`, `checking`, `current`, `business`, `personal`, `other` | +| `BenefitsTypeEnum` | `retirement_savings`, `health_savings`, `other`, `health_insurance`, `insurance` | +| `ClearingCodeTypeEnum` | `sort_code`, `building_society_reference`, `aba_routing`, `chips_participant_id`, `transit_number`, `institution_number`, `bsb_code`, `ifsc_code`, `micr_code`, `clabe`, `cnaps_code`, `ch_bank_clearing_code`, `ch_sic_code`, `zengin_code`, `nz_clearing_code`, `hk_bank_code`, `blz`, `at_bankleitzahl`, `nuban`, `se_bankgiro_code` | +| `ConfidentialEnumApiModel` | `true`, `false` | +| `ContractTypeEnum` | `full_time`, `shifts`, `part_time` | +| `CountryCodeEnum` | `AF`, `AL`, `DZ`, `AS`, `AD`, `AO`, `AI`, `AQ`, `AG`, `AR`, `AM`, `AW`, `AU`, `AT`, `AZ`, `BS`, `BH`, `BD`, `BB`, `BY`, `BE`, `BZ`, `BJ`, `BM`, `BT`, `BO`, `BQ`, `BA`, `BW`, `BV`, `BR`, `IO`, `BN`, `BG`, `BF`, `BI`, `KH`, `CM`, `CA`, `CV`, `KY`, `CF`, `TD`, `CL`, `CN`, `CX`, `CC`, `CO`, `KM`, `CG`, `CD`, `CK`, `CR`, `HR`, `CU`, `CW`, `CY`, `CZ`, `CI`, `DK`, `DJ`, `DM`, `DO`, `EC`, `EG`, `SV`, `GQ`, `ER`, `EE`, `ET`, `FK`, `FO`, `FJ`, `FI`, `FR`, `GF`, `PF`, `TF`, `GA`, `GM`, `GE`, `DE`, `GH`, `GI`, `GR`, `GL`, `GD`, `GP`, `GU`, `GT`, `GG`, `GN`, `GW`, `GY`, `HT`, `HM`, `VA`, `HN`, `HK`, `HU`, `IS`, `IN`, `ID`, `IR`, `IQ`, `IE`, `IM`, `IL`, `IT`, `JM`, `JP`, `JE`, `JO`, `KZ`, `KE`, `KI`, `KP`, `KR`, `KW`, `KG`, `LA`, `LV`, `LB`, `LS`, `LR`, `LY`, `LI`, `LT`, `LU`, `MO`, `MK`, `MG`, `MW`, `MY`, `MV`, `ML`, `MT`, `MH`, `MQ`, `MR`, `MU`, `YT`, `MX`, `FM`, `MD`, `MC`, `MN`, `ME`, `MS`, `MA`, `MZ`, `MM`, `NA`, `NR`, `NP`, `NL`, `NC`, `NZ`, `NI`, `NE`, `NG`, `NU`, `NF`, `MP`, `NO`, `OM`, `PK`, `PW`, `PS`, `PA`, `PG`, `PY`, `PE`, `PH`, `PN`, `PL`, `PT`, `PR`, `QA`, `RO`, `RU`, `RW`, `RE`, `BL`, `SH`, `KN`, `LC`, `MF`, `PM`, `VC`, `WS`, `SM`, `ST`, `SA`, `SN`, `RS`, `SC`, `SL`, `SG`, `SX`, `SK`, `SI`, `SB`, `SO`, `ZA`, `GS`, `SS`, `ES`, `LK`, `SD`, `SR`, `SJ`, `SZ`, `SE`, `CH`, `SY`, `TW`, `TJ`, `TZ`, `TH`, `TL`, `TG`, `TK`, `TO`, `TT`, `TN`, `TR`, `TM`, `TC`, `TV`, `UG`, `UA`, `AE`, `GB`, `US`, `UM`, `UY`, `UZ`, `VU`, `VE`, `VN`, `VG`, `VI`, `WF`, `EH`, `YE`, `ZM`, `ZW` | +| `CurrencyCodeEnum` | `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLP`, `CNY`, `COP`, `CRC`, `CUC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLL`, `SOS`, `SRD`, `SSP`, `STN`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XDR`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL` | +| `CustomFieldTypeEnum` | `date`, `float`, `integer`, `list`, `checkbox`, `text`, `boolean`, `single_select`, `multi_select`, `url`, `other` | +| `DepartmentTypeEnum` | `department`, `company`, `division`, `group`, `project`, `team` | +| `DivisionTypeEnum` | `department`, `company`, `division`, `group`, `project`, `cost_center`, `team` | +| `EmploymentScheduleTypeEnum` | `full_time`, `shifts`, `part_time` | +| `EmploymentStatusEnum` | `active`, `pending`, `terminated`, `leave`, `inactive`, `unknown` | +| `EmploymentTypeEnum` | `contractor`, `intern`, `permanent`, `apprentice`, `freelance`, `terminated`, `temporary`, `seasonal`, `volunteer`, `probation`, `internal`, `external`, `expatriate`, `employer_of_record`, `casual`, `Programme` | +| `EthnicityEnum` | `white`, `black_or_african_american`, `asian`, `hispanic_or_latino`, `american_indian_or_alaska_native`, `native_hawaiian_or_pacific_islander`, `two_or_more_races`, `not_disclosed`, `other` | +| `FileFormatEnum` | `ez`, `aw`, `atom`, `atomcat`, `atomdeleted`, `atomsvc`, `dwd`, `held`, `rsat`, `bdoc`, `xcs`, `ccxml`, `cdfx`, `cdmia`, `cdmic`, `cdmid`, `cdmio`, `cdmiq`, `cu`, `mpd`, `davmount`, `dbk`, `dssc`, `xdssc`, `es`, `ecma`, `emma`, `emotionml`, `epub`, `exi`, `exp`, `fdt`, `pfr`, `geojson`, `gml`, `gpx`, `gxf`, `gz`, `hjson`, `stk`, `ink`, `inkml`, `ipfix`, `its`, `jar`, `war`, `ear`, `ser`, `class`, `js`, `mjs`, `json`, `map`, `json5`, `jsonml`, `jsonld`, `lgr`, `lostxml`, `hqx`, `cpt`, `mads`, `webmanifest`, `mrc`, `mrcx`, `ma`, `nb`, `mb`, `mathml`, `mbox`, `mscml`, `metalink`, `meta4`, `mets`, `maei`, `musd`, `mods`, `m21`, `mp21`, `mp4s`, `m4p`, `doc`, `dot`, `mxf`, `nq`, `nt`, `cjs`, `bin`, `dms`, `lrf`, `mar`, `so`, `dist`, `distz`, `pkg`, `bpk`, `dump`, `elc`, `deploy`, `exe`, `dll`, `deb`, `dmg`, `iso`, `img`, `msi`, `msp`, `msm`, `buffer`, `oda`, `opf`, `ogx`, `omdoc`, `onetoc`, `onetoc2`, `onetmp`, `onepkg`, `oxps`, `relo`, `xer`, `pdf`, `pgp`, `asc`, `sig`, `prf`, `p10`, `p7m`, `p7c`, `p7s`, `p8`, `ac`, `cer`, `crl`, `pkipath`, `pki`, `pls`, `ai`, `eps`, `ps`, `provx`, `pskcxml`, `raml`, `rdf`, `owl`, `rif`, `rnc`, `rl`, `rld`, `rs`, `rapd`, `sls`, `rusd`, `gbr`, `mft`, `roa`, `rsd`, `rss`, `rtf`, `sbml`, `scq`, `scs`, `spq`, `spp`, `sdp`, `senmlx`, `sensmlx`, `setpay`, `setreg`, `shf`, `siv`, `sieve`, `smi`, `smil`, `rq`, `srx`, `gram`, `grxml`, `sru`, `ssdl`, `ssml`, `swidtag`, `tei`, `teicorpus`, `tfi`, `tsd`, `toml`, `trig`, `ttml`, `ubj`, `rsheet`, `td`, `vxml`, `wasm`, `wgt`, `hlp`, `wsdl`, `wspolicy`, `xaml`, `xav`, `xca`, `xdf`, `xel`, `xns`, `xenc`, `xhtml`, `xht`, `xlf`, `xml`, `xsl`, `xsd`, `rng`, `dtd`, `xop`, `xpl`, `*xsl`, `xslt`, `xspf`, `mxml`, `xhvml`, `xvml`, `xvm`, `yang`, `yin`, `zip`, `*3gpp`, `adp`, `amr`, `au`, `snd`, `mid`, `midi`, `kar`, `rmi`, `mxmf`, `*mp3`, `m4a`, `mp4a`, `mpga`, `mp2`, `mp2a`, `mp3`, `m2a`, `m3a`, `oga`, `ogg`, `spx`, `opus`, `s3m`, `sil`, `wav`, `*wav`, `weba`, `xm`, `ttc`, `otf`, `ttf`, `woff`, `woff2`, `exr`, `apng`, `avif`, `bmp`, `cgm`, `drle`, `emf`, `fits`, `g3`, `gif`, `heic`, `heics`, `heif`, `heifs`, `hej2`, `hsj2`, `ief`, `jls`, `jp2`, `jpg2`, `jpeg`, `jpg`, `jpe`, `jph`, `jhc`, `jpm`, `jpx`, `jpf`, `jxr`, `jxra`, `jxrs`, `jxs`, `jxsc`, `jxsi`, `jxss`, `ktx`, `ktx2`, `png`, `sgi`, `svg`, `svgz`, `t38`, `tif`, `tiff`, `tfx`, `webp`, `wmf`, `disposition-notification`, `u8msg`, `u8dsn`, `u8mdn`, `u8hdr`, `eml`, `mime`, `3mf`, `gltf`, `glb`, `igs`, `iges`, `msh`, `mesh`, `silo`, `mtl`, `obj`, `stpx`, `stpz`, `stpxz`, `stl`, `wrl`, `vrml`, `*x3db`, `x3dbz`, `x3db`, `*x3dv`, `x3dvz`, `x3d`, `x3dz`, `x3dv`, `appcache`, `manifest`, `ics`, `ifb`, `coffee`, `litcoffee`, `css`, `csv`, `html`, `htm`, `shtml`, `jade`, `jsx`, `less`, `markdown`, `md`, `mml`, `mdx`, `n3`, `txt`, `text`, `conf`, `def`, `list`, `log`, `in`, `ini`, `rtx`, `*rtf`, `sgml`, `sgm`, `shex`, `slim`, `slm`, `spdx`, `stylus`, `styl`, `tsv`, `t`, `tr`, `roff`, `man`, `me`, `ms`, `ttl`, `uri`, `uris`, `urls`, `vcard`, `vtt`, `*xml`, `yaml`, `yml`, `3gp`, `3gpp`, `3g2`, `h261`, `h263`, `h264`, `m4s`, `jpgv`, `*jpm`, `jpgm`, `mj2`, `mjp2`, `ts`, `mp4`, `mp4v`, `mpg4`, `mpeg`, `mpg`, `mpe`, `m1v`, `m2v`, `ogv`, `qt`, `mov`, `webm`, `cww`, `1km`, `plb`, `psb`, `pvb`, `tcap`, `pwn`, `aso`, `imp`, `acu`, `atc`, `acutc`, `air`, `fcdt`, `fxp`, `fxpl`, `xdp`, `xfdf`, `ahead`, `azf`, `azs`, `azw`, `acc`, `ami`, `apk`, `cii`, `fti`, `atx`, `mpkg`, `key`, `m3u8`, `numbers`, `pages`, `pkpass`, `swi`, `iota`, `aep`, `bmml`, `mpm`, `bmi`, `rep`, `cdxml`, `mmd`, `cdy`, `csl`, `cla`, `rp9`, `c4g`, `c4d`, `c4f`, `c4p`, `c4u`, `c11amc`, `c11amz`, `csp`, `cdbcmsg`, `cmc`, `clkx`, `clkk`, `clkp`, `clkt`, `clkw`, `wbs`, `pml`, `ppd`, `car`, `pcurl`, `dart`, `rdz`, `dbf`, `uvf`, `uvvf`, `uvd`, `uvvd`, `uvt`, `uvvt`, `uvx`, `uvvx`, `uvz`, `uvvz`, `fe_launch`, `dna`, `mlp`, `mle`, `dpg`, `dfac`, `kpxx`, `ait`, `svc`, `geo`, `mag`, `nml`, `esf`, `msf`, `qam`, `slt`, `ssf`, `es3`, `et3`, `ez2`, `ez3`, `fdf`, `mseed`, `seed`, `dataless`, `gph`, `ftc`, `fm`, `frame`, `maker`, `book`, `fnc`, `ltf`, `fsc`, `oas`, `oa2`, `oa3`, `fg5`, `bh2`, `ddd`, `xdw`, `xbd`, `fzs`, `txd`, `ggb`, `ggt`, `gex`, `gre`, `gxt`, `g2w`, `g3w`, `gmx`, `gdoc`, `gslides`, `gsheet`, `kml`, `kmz`, `gqf`, `gqs`, `gac`, `ghf`, `gim`, `grv`, `gtm`, `tpl`, `vcg`, `hal`, `zmm`, `hbci`, `les`, `hpgl`, `hpid`, `hps`, `jlt`, `pcl`, `pclxl`, `sfd-hdstx`, `mpy`, `afp`, `listafp`, `list3820`, `irm`, `sc`, `icc`, `icm`, `igl`, `ivp`, `ivu`, `igm`, `xpw`, `xpx`, `i2g`, `qbo`, `qfx`, `rcprofile`, `irp`, `xpr`, `fcs`, `jam`, `rms`, `jisp`, `joda`, `ktz`, `ktr`, `karbon`, `chrt`, `kfo`, `flw`, `kon`, `kpr`, `kpt`, `ksp`, `kwd`, `kwt`, `htke`, `kia`, `kne`, `knp`, `skp`, `skd`, `skt`, `skm`, `sse`, `lasxml`, `lbd`, `lbe`, `apr`, `pre`, `nsf`, `org`, `scm`, `lwp`, `portpkg`, `mvt`, `mcd`, `mc1`, `cdkey`, `mwf`, `mfm`, `flo`, `igx`, `mif`, `daf`, `dis`, `mbk`, `mqy`, `msl`, `plc`, `txf`, `mpn`, `mpc`, `xul`, `cil`, `cab`, `xls`, `xlm`, `xla`, `xlc`, `xlt`, `xlw`, `xlam`, `xlsb`, `xlsm`, `xltm`, `eot`, `chm`, `ims`, `lrm`, `thmx`, `msg`, `cat`, `*stl`, `ppt`, `pps`, `pot`, `ppam`, `pptm`, `sldm`, `ppsm`, `potm`, `mpp`, `mpt`, `docm`, `dotm`, `wps`, `wks`, `wcm`, `wdb`, `wpl`, `xps`, `mseq`, `mus`, `msty`, `taglet`, `nlu`, `ntf`, `nitf`, `nnd`, `nns`, `nnw`, `*ac`, `ngdat`, `n-gage`, `rpst`, `rpss`, `edm`, `edx`, `ext`, `odc`, `otc`, `odb`, `odf`, `odft`, `odg`, `otg`, `odi`, `oti`, `odp`, `otp`, `ods`, `ots`, `odt`, `odm`, `ott`, `oth`, `xo`, `dd2`, `obgx`, `oxt`, `osm`, `pptx`, `sldx`, `ppsx`, `potx`, `xlsx`, `xltx`, `docx`, `dotx`, `mgp`, `dp`, `esa`, `pdb`, `pqa`, `oprc`, `paw`, `str`, `ei6`, `efif`, `wg`, `plf`, `pbd`, `box`, `mgz`, `qps`, `ptid`, `qxd`, `qxt`, `qwd`, `qwt`, `qxl`, `qxb`, `rar`, `bed`, `mxl`, `musicxml`, `cryptonote`, `cod`, `rm`, `rmvb`, `link66`, `st`, `see`, `sema`, `semd`, `semf`, `ifm`, `itp`, `iif`, `ipk`, `twd`, `twds`, `mmf`, `teacher`, `fo`, `sdkm`, `sdkd`, `dxp`, `sfs`, `sdc`, `sda`, `sdd`, `smf`, `sdw`, `vor`, `sgl`, `smzip`, `sm`, `wadl`, `sxc`, `stc`, `sxd`, `std`, `sxi`, `sti`, `sxm`, `sxw`, `sxg`, `stw`, `sus`, `susp`, `svd`, `sis`, `sisx`, `xsm`, `bdm`, `xdm`, `ddf`, `tao`, `pcap`, `cap`, `dmp`, `tmo`, `tpt`, `mxs`, `tra`, `ufd`, `ufdl`, `utz`, `umj`, `unityweb`, `uoml`, `vcx`, `vsd`, `vst`, `vss`, `vsw`, `vis`, `vsf`, `wbxml`, `wmlc`, `wmlsc`, `wtb`, `nbp`, `wpd`, `wqd`, `stf`, `xar`, `xfdl`, `hvd`, `hvs`, `hvp`, `osf`, `osfpvg`, `saf`, `spf`, `cmp`, `zir`, `zirz`, `zaz`, `7z`, `abw`, `ace`, `*dmg`, `arj`, `aab`, `x32`, `u32`, `vox`, `aam`, `aas`, `bcpio`, `*bdoc`, `torrent`, `blb`, `blorb`, `bz`, `bz2`, `boz`, `cbr`, `cba`, `cbt`, `cbz`, `cb7`, `vcd`, `cfs`, `chat`, `pgn`, `crx`, `cco`, `nsc`, `cpio`, `csh`, `*deb`, `udeb`, `dgc`, `dir`, `dcr`, `dxr`, `cst`, `cct`, `cxt`, `w3d`, `fgd`, `swa`, `wad`, `ncx`, `dtb`, `res`, `dvi`, `evy`, `eva`, `bdf`, `gsf`, `psf`, `pcf`, `snf`, `pfa`, `pfb`, `pfm`, `afm`, `arc`, `spl`, `gca`, `ulx`, `gnumeric`, `gramps`, `gtar`, `hdf`, `php`, `install`, `*iso`, `*key`, `*numbers`, `*pages`, `jardiff`, `jnlp`, `kdbx`, `latex`, `luac`, `lzh`, `lha`, `run`, `mie`, `prc`, `mobi`, `application`, `lnk`, `wmd`, `wmz`, `xbap`, `mdb`, `obd`, `crd`, `clp`, `*exe`, `*dll`, `com`, `bat`, `*msi`, `mvb`, `m13`, `m14`, `*wmf`, `*wmz`, `*emf`, `emz`, `mny`, `pub`, `scd`, `trm`, `wri`, `nc`, `cdf`, `pac`, `nzb`, `pl`, `pm`, `*prc`, `*pdb`, `p12`, `pfx`, `p7b`, `spc`, `p7r`, `*rar`, `rpm`, `ris`, `sea`, `sh`, `shar`, `swf`, `xap`, `sql`, `sit`, `sitx`, `srt`, `sv4cpio`, `sv4crc`, `t3`, `gam`, `tar`, `tcl`, `tk`, `tex`, `tfm`, `texinfo`, `texi`, `*obj`, `ustar`, `hdd`, `ova`, `ovf`, `vbox`, `vbox-extpack`, `vdi`, `vhd`, `vmdk`, `src`, `webapp`, `der`, `crt`, `pem`, `fig`, `*xlf`, `xpi`, `xz`, `z1`, `z2`, `z3`, `z4`, `z5`, `z6`, `z7`, `z8`, `uva`, `uvva`, `eol`, `dra`, `dts`, `dtshd`, `lvp`, `pya`, `ecelp4800`, `ecelp7470`, `ecelp9600`, `rip`, `aac`, `aif`, `aiff`, `aifc`, `caf`, `flac`, `*m4a`, `mka`, `m3u`, `wax`, `wma`, `ram`, `ra`, `rmp`, `*ra`, `cdx`, `cif`, `cmdf`, `cml`, `csml`, `xyz`, `btif`, `pti`, `psd`, `azv`, `uvi`, `uvvi`, `uvg`, `uvvg`, `djvu`, `djv`, `*sub`, `dwg`, `dxf`, `fbs`, `fpx`, `fst`, `mmr`, `rlc`, `ico`, `dds`, `mdi`, `wdp`, `npx`, `b16`, `tap`, `vtf`, `wbmp`, `xif`, `pcx`, `3ds`, `ras`, `cmx`, `fh`, `fhc`, `fh4`, `fh5`, `fh7`, `*ico`, `jng`, `sid`, `*bmp`, `*pcx`, `pic`, `pct`, `pnm`, `pbm`, `pgm`, `ppm`, `rgb`, `tga`, `xbm`, `xpm`, `xwd`, `wsc`, `dae`, `dwf`, `gdl`, `gtw`, `mts`, `ogex`, `x_b`, `x_t`, `vds`, `usdz`, `bsp`, `vtu`, `dsc`, `curl`, `dcurl`, `mcurl`, `scurl`, `sub`, `fly`, `flx`, `gv`, `3dml`, `spot`, `jad`, `wml`, `wmls`, `s`, `asm`, `c`, `cc`, `cxx`, `cpp`, `h`, `hh`, `dic`, `htc`, `f`, `for`, `f77`, `f90`, `hbs`, `java`, `lua`, `mkd`, `nfo`, `opml`, `*org`, `p`, `pas`, `pde`, `sass`, `scss`, `etx`, `sfv`, `ymp`, `uu`, `vcs`, `vcf`, `uvh`, `uvvh`, `uvm`, `uvvm`, `uvp`, `uvvp`, `uvs`, `uvvs`, `uvv`, `uvvv`, `dvb`, `fvt`, `mxu`, `m4u`, `pyv`, `uvu`, `uvvu`, `viv`, `f4v`, `fli`, `flv`, `m4v`, `mkv`, `mk3d`, `mks`, `mng`, `asf`, `asx`, `vob`, `wm`, `wmv`, `wmx`, `wvx`, `avi`, `movie`, `smv`, `ice`, `mht` | +| `GenderEnum` | `male`, `female`, `non_binary`, `other`, `not_disclosed`, `diverse` | +| `GroupTypeEnum` | `workspace`, `team`, `department`, `group`, `organization`, `cost_center` | +| `HrisDocumentTypeEnum` | `application`, `academic`, `contract`, `certificates`, `visa`, `passport`, `driver_license`, `payslip`, `payroll`, `appraisal`, `resume`, `policy`, `cover_letter`, `offer_letter`, `policy_agreement`, `home_address`, `national_id`, `confidential`, `signed`, `shared`, `other`, `benefit`, `id_verification`, `background_check` | +| `HrisDocumentsUploadCategoryEnumApiModel` | `application`, `academic`, `contract`, `certificates`, `visa`, `passport`, `driver_license`, `payslip`, `payroll`, `appraisal`, `resume`, `policy`, `cover_letter`, `offer_letter`, `policy_agreement`, `home_address`, `national_id`, `confidential`, `signed`, `shared`, `other`, `benefit`, `id_verification`, `background_check` | +| `HrisJobStatusEnum` | `draft`, `pending`, `archived`, `closed`, `open`, `deleted` | +| `LanguageEnum` | `ar_AR`, `aa_ER`, `af_NA`, `af_ZA`, `am_ET`, `ar_AE`, `ar_BH`, `ar_DJ`, `ar_DZ`, `ar_EG`, `ar_ER`, `ar_IQ`, `ar_JO`, `ar_KM`, `ar_KW`, `ar_LB`, `ar_LY`, `ar_MA`, `ar_MR`, `ar_OM`, `ar_PS`, `ar_QA`, `ar_SA`, `ar_SD`, `ar_SY`, `ar_TD`, `ar_TN`, `ar_YE`, `ay_BO`, `ay_PE`, `az_AZ`, `az_IR`, `be_BY`, `bg_BG`, `bi_VU`, `bn_BD`, `bn_IN`, `bs_BA`, `bs-ME`, `byn_ER`, `ca_AD`, `ca_ES`, `ca_FR`, `ca_IT`, `ch_GU`, `cs_CZ`, `da_DK`, `de_AT`, `de_BE`, `de_CH`, `de_DE`, `de_LI`, `de_LU`, `de_VA`, `de_MV`, `dv_MV`, `dz_BT`, `el_CY`, `el_GR`, `en_AG`, `en_AI`, `en_AS`, `en_AU`, `en_BB`, `en_BE`, `en_BM`, `en_BS`, `en_BW`, `en_BZ`, `en_CA`, `en_CC`, `en_CK`, `en_CM`, `en_CW`, `en_CX`, `en_DG`, `en_DM`, `en_ER`, `en_FJ`, `en_FK`, `en_FM`, `en_GB`, `en_GD`, `en_GG`, `en_GH`, `en_GI`, `en_GM`, `en_GS`, `en_GU`, `en_GY`, `en_HK`, `en_IE`, `en_IM`, `en_IN`, `en_IO`, `en_JE`, `en_JM`, `en_KE`, `en_KI`, `en_KN`, `en_KY`, `en_LC`, `en_LR`, `en_LS`, `en_MF`, `en_MG`, `en_MH`, `en_MO`, `en_MP`, `en_MS`, `en_MT`, `en_MU`, `en_MW`, `en_MY`, `en_NA`, `en_NF`, `en_NG`, `en_NL`, `en_NR`, `en_NU`, `en_NZ`, `en_PG`, `en_PH`, `en_PK`, `en_PN`, `en_PR`, `en_PW`, `en_RW`, `en_SB`, `en_SC`, `en_SD`, `en_SG`, `en_SH`, `en_SL`, `en_SS`, `en_SX`, `en_SZ`, `en_TC`, `en_TK`, `en_TO`, `en_TT`, `en_TV`, `en_TZ`, `en_UG`, `en_UM`, `en_US`, `en_VC`, `en_VG`, `en_VI`, `en_VU`, `en_WS`, `en_ZA`, `en_ZM`, `en_ZW`, `es_AR`, `es_BO`, `es_BZ`, `es_CL`, `es_CO`, `es_CR`, `es_CU`, `es_DO`, `es_EA`, `es_EC`, `es_EH`, `es_ES`, `es_GQ`, `es_GT`, `es_HN`, `es_IC`, `es_LA`, `es_MX`, `es_NI`, `es_PA`, `es_PE`, `es_PH`, `es_PR`, `es_PY`, `es_SV`, `es_US`, `es_UY`, `es_VE`, `et_EE`, `fa_AF`, `fa_IR`, `fan_GA`, `ff_CM`, `ff_GN`, `ff_MR`, `ff_SN`, `ff_BF`, `fi_FI`, `fj_FJ`, `fo_FO`, `fr_BE`, `fr_BF`, `fr_BI`, `fr_BJ`, `fr_BL`, `fr_CA`, `fr_CD`, `fr_CF`, `fr_CG`, `fr_CH`, `fr_CI`, `fr_CM`, `fr_DJ`, `fr_DZ`, `fr_FR`, `fr_GA`, `fr_GF`, `fr_GG`, `fr_GN`, `fr_GP`, `fr_GQ`, `fr_HT`, `fr_KM`, `fr_JE`, `fr_LU`, `fr_LB`, `fr_MA`, `fr_MC`, `fr_MF`, `fr_MG`, `fr_ML`, `fr_MQ`, `fr_MR`, `fr_MU`, `fr_NC`, `fr_NE`, `fr_PF`, `fr_PM`, `fr_RE`, `fr_RW`, `fr_SC`, `fr_SN`, `fr_SY`, `fr_TD`, `fr_TF`, `fr_TG`, `fr_TN`, `fr_VU`, `fr_VA`, `fr_WF`, `fr_YT`, `ga_IE`, `gn_PY`, `gn_AR`, `gu_IN`, `gv_IM`, `he_IL`, `hi_IN`, `hr_BA`, `hr_HR`, `hr_ME`, `ht_HT`, `hu_HU`, `hy_AM`, `hy_CY`, `id_ID`, `is_IS`, `it_CH`, `it_IT`, `it_SM`, `it_VA`, `ja_JP`, `ka_GE`, `kg_CD`, `kk_KZ`, `kl_GL`, `km_KH`, `ko_KP`, `ko_KR`, `ku_IQ`, `ky_KG`, `la_VA`, `lb_LU`, `ln_AO`, `ln_CD`, `ln_CF`, `ln_CG`, `lo_LA`, `lt_LT`, `lu_CD`, `lv_LV`, `mg_MG`, `mh_MH`, `mi_NZ`, `mk_MK`, `mn_MN`, `mr_IN`, `ms_BN`, `ms_MY`, `ms_SG`, `mt_MT`, `my_MM`, `nb_NO`, `nb_BV`, `nb_ZW`, `ne_NP`, `nl_AW`, `nl_BE`, `nl_BQ`, `nl_CW`, `nl_NL`, `nl_SR`, `nl_SX`, `nl_MF`, `nn_NO`, `nn_BV`, `no_NO`, `no_BV`, `no_SJ`, `nr_ZA`, `ny_MW`, `pa_IN`, `pa_PK`, `pl_PL`, `ps_AF`, `pt_AO`, `pt_BR`, `pt_CH`, `pt_CV`, `pt_GQ`, `pt_GW`, `pt_LU`, `pt_MO`, `pt_MZ`, `pt_PT`, `pt_ST`, `pt_TL`, `qu_BO`, `qu_EC`, `qu_PE`, `rar_CK`, `rm_CH`, `rup_MK`, `ro_MD`, `ro_RO`, `ru_BY`, `ru_KG`, `ru_KZ`, `ru_MD`, `ru_RU`, `ru_UA`, `ru_AQ`, `ru_TJ`, `ru_TM`, `ru_UZ`, `rw_RW`, `se_SE`, `sg_CF`, `si_LK`, `sk_SK`, `sl_SI`, `sm_AS`, `sm_WS`, `sn_ZW`, `so_DJ`, `so_ET`, `so_KE`, `so_SO`, `sq_AL`, `sq_ME`, `sq_XK`, `sr_BA`, `sr_ME`, `sr_RS`, `sr_XK`, `ss_SZ`, `ss_ZA`, `sv_AX`, `sv_FI`, `sv_SE`, `sw_KE`, `sw_TZ`, `sw_UG`, `sw_CD`, `ta_IN`, `ta_MY`, `ta_SG`, `ta_LK`, `te_IN`, `tg_TJ`, `th_TH`, `ti_ER`, `ti_ET`, `tig_ER`, `tk_TM`, `tk_AF`, `tn_BW`, `tn_ZA`, `to_TO`, `tr_CY`, `tr_TR`, `ts_ZA`, `uk_UA`, `ur_IN`, `ur_PK`, `uz_AF`, `uz_UZ`, `ve_ZA`, `vi_VN`, `xh_ZA`, `zh_CN`, `zh_HK`, `zh_MO`, `zh_SG`, `zh_TW`, `zu_ZA` | +| `LocationTypeEnum` | `home`, `work` | +| `MaritalStatusEnum` | `single`, `married`, `common_law`, `divorced`, `widowed`, `domestic_partnership`, `separated`, `other`, `not_disclosed` | +| `NationalIdentityNumberTypeEnumApiModel` | `ssn`, `nin`, `sin`, `nid`, `pin`, `pn`, `umcn`, `pic`, `ric`, `idnum`, `cid`, `nidnr`, `pan`, `aadhaar`, `epic`, `ptn`, `itin`, `tin`, `uprc`, `pcode`, `ssi`, `cedula`, `passport`, `voterid`, `ntin`, `bn`, `fnr`, `mva`, `civil_id`, `cnic`, `nric`, `fin`, `uen`, `registrationnumber`, `nic`, `personnummer`, `ahv`, `id`, `eid`, `va`, `pid`, `nrt`, `nipt`, `cbu`, `cuit`, `dni`, `businessid`, `vnr`, `abn`, `acn`, `tfn`, `jmbg`, `bis`, `insz`, `nn`, `egn`, `pnf`, `vat`, `cnpj`, `unp`, `gst`, `pst`, `qst`, `ni`, `dic`, `rc`, `uid`, `rut`, `uscc`, `cpf`, `cpj`, `cr`, `stnr`, `svnr`, `ncf`, `rnc`, `nif`, `ci`, `ik`, `kmkr`, `registrikood`, `tn`, `ruc`, `nit`, `alv`, `hetu`, `ytunnus`, `vn`, `utr`, `nifp`, `amka`, `cui`, `nir`, `siren`, `siret`, `tva`, `oib`, `hkid`, `anum`, `kennitala`, `vsk`, `npwp`, `pps`, `gstin`, `idnr`, `hr`, `aic`, `codicefiscale`, `iva`, `peid`, `asmens`, `pvm`, `ctps`, `vrn`, `vtk`, `int`, `tk`, `pas`, `rne`, `rg`, `nci`, `crnm`, `pis`, `insee`, `tax`, `mpf`, `epfo`, `esi`, `pran`, `uan`, `idk`, `bsn`, `mid`, `sss`, `nie`, `nss`, `arc`, `curp`, `imss`, `rfc`, `ein`, `other`, `unknown` | +| `PayFrequencyEnum` | `hourly`, `weekly`, `bi_weekly`, `four_weekly`, `semi_monthly`, `monthly`, `bi_monthly`, `quarterly`, `semi_annually`, `yearly`, `thirteen_monthly`, `pro_rata`, `half_yearly`, `daily`, `fixed` | +| `PayPeriodEnum` | `hour`, `day`, `week`, `every_two_weeks`, `month`, `quarter`, `every_six_months`, `year` | +| `PositionStatusEnum` | `open`, `draft`, `closed`, `paused` | +| `PreferredLanguageEnum` | `aar`, `afr`, `amh`, `ara`, `aym`, `aze`, `bel`, `bul`, `bis`, `ben`, `bos`, `byn`, `cat`, `cha`, `ces`, `dan`, `deu`, `div`, `dzo`, `ell`, `eng`, `spa`, `est`, `fas`, `fan`, `ful`, `fin`, `fij`, `fao`, `fra`, `gle`, `grn`, `guj`, `glv`, `heb`, `hin`, `hrv`, `hat`, `hun`, `hye`, `ind`, `isl`, `ita`, `jpn`, `kat`, `kon`, `kaz`, `kal`, `khm`, `kor`, `kur`, `kir`, `lat`, `ltz`, `lin`, `lao`, `lit`, `lub`, `lav`, `mlg`, `mah`, `mri`, `mkd`, `mon`, `mar`, `msa`, `mlt`, `mya`, `nob`, `nep`, `nld`, `nno`, `nor`, `nbl`, `nya`, `pan`, `pol`, `pus`, `por`, `que`, `rar`, `roh`, `rup`, `ron`, `rus`, `kin`, `sme`, `sag`, `sin`, `slk`, `slv`, `smo`, `sna`, `som`, `sqi`, `srp`, `ssw`, `swe`, `swa`, `tam`, `tel`, `tgk`, `tha`, `tir`, `tig`, `tuk`, `tsn`, `ton`, `tur`, `tso`, `ukr`, `urd`, `uzb`, `ven`, `vie`, `xho`, `zho`, `zul` | +| `Proficiency` | `1`, `2`, `3`, `4`, `5` | +| `RoleTypeEnum` | `admin`, `viewer`, `editor`, `basic`, `guest`, `unassigned`, `restricted` | +| `ShiftApprovalStatusEnum` | `pending`, `approved`, `rejected` | +| `ShiftStatusEnum` | `draft`, `published`, `confirmed`, `cancelled` | +| `TaskStatusEnum` | `open`, `in_progress`, `blocked`, `completed`, `cancelled` | +| `TaskTypeEnum` | `action`, `review`, `acknowledgment`, `edit`, `approve` | +| `TeamTypeEnum` | `team` | +| `TimeEntryStatusEnum` | `approved` | +| `TimeOffBalanceUnitEnum` | `minutes`, `hours`, `days`, `weeks`, `months`, `years`, `unknown` | +| `TimeOffPolicyTypeEnum` | `sick`, `vacation`, `long_term_disability`, `short_term_disability`, `absent`, `comp_time`, `training`, `annual_leave`, `leave_of_absence`, `break`, `child_care_leave`, `maternity_leave`, `jury_duty`, `sabbatical`, `accident`, `paid`, `unpaid`, `holiday`, `personal`, `in_lieu`, `bereavement`, `other` | +| `TimeOffStatusEnum` | `approved`, `cancelled`, `rejected`, `pending`, `deleted`, `draft` | +| `TimeOffTypeEnum` | `sick`, `vacation`, `long_term_disability`, `short_term_disability`, `absent`, `comp_time`, `training`, `annual_leave`, `leave_of_absence`, `break`, `child_care_leave`, `maternity_leave`, `jury_duty`, `sabbatical`, `accident`, `paid`, `unpaid`, `holiday`, `personal`, `in_lieu`, `bereavement`, `other` | +| `TypeEnum` | `contractor`, `intern`, `permanent`, `apprentice`, `freelance`, `terminated`, `temporary`, `seasonal`, `volunteer`, `probation`, `internal`, `external`, `expatriate`, `employer_of_record`, `casual`, `Programme` | +| `WorkEligibilityTypeEnum` | `visa`, `passport`, `driver_license`, `birth_certificate`, `other` | +| `WorkTimeUnitEnum` | `day`, `week`, `month`, `year` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/iam.md b/.claude/plugins/stackone-connector-builder/references/schemas/iam.md new file mode 100644 index 0000000..4ee8714 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/iam.md @@ -0,0 +1,165 @@ +# IAM (Identity & Access Management) — Schema Reference + +> Manage users, groups, roles and permissions. + +> **OAS source:** `https://docs.stackone.com/iam/api-reference/iam.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/iam/users` | List Users | +| `GET` | `/unified/iam/users/{id}` | Get User | +| `PATCH` | `/unified/iam/users/{id}` | Update User | +| `DELETE` | `/unified/iam/users/{id}` | Delete User | +| `GET` | `/unified/iam/roles` | List Roles | +| `GET` | `/unified/iam/roles/{id}` | Get Role | +| `GET` | `/unified/iam/groups` | List Groups | +| `GET` | `/unified/iam/groups/{id}` | Get Group | +| `GET` | `/unified/iam/policies` | List Policies | +| `GET` | `/unified/iam/policies/{id}` | Get Policy | + +--- + +## Models + +### `Content` +| Field | Type | Description | +|-------|------|-------------| +| `url` | `string` | URL where the file content is located | +| `unified_url` | `string` | Unified download URL for retrieving file content. | +| `file_format` | `FileFormatEnum` | The file format of the file | + +### `File` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the file | +| `category` | `FileCategoryEnumApiModel` | The category of the file | +| `contents` | `array` | The content of the file. Deprecated, use `url` and `file_format` one level up instead | +| `category_id` | `string` | The categoryId of the documents | +| `created_at` | `string` | The creation date of the file | +| `updated_at` | `string` | The update date of the file | +| `remote_url` | `string` | URL where the file content is located | +| `file_format` | `FileFormatEnum` | The file format of the file | + +### `FileCategoryEnumApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `value` | `string` | The category of the file | +| `source_value` | `object` | | + +### `IamGroup` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `description` | `string` | | +| `type` | `GroupTypeEnum` | | +| `roles` | `array` | | +| `parent_id` | `string` | The parent group id for when a group belongs to another group. | +| `remote_parent_id` | `string` | Provider's unique identifier of the parent group id for when a group belongs to another group. | +| `created_at` | `string` | | +| `updated_at` | `string` | | + +### `IamPermission` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the permission. | +| `type` | `IamPermissionTypeEnum` | | +| `resources` | `array` | The resources that the permission applies to. | +| `description` | `string` | | +| `created_at` | `string` | | +| `updated_at` | `string` | | + +### `IamPolicy` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the policy. | +| `permissions` | `array` | The set of permissions associated with the policy. | +| `description` | `string` | | +| `created_at` | `string` | | +| `updated_at` | `string` | | + +### `IamResource` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the resource. | +| `location` | `string` | The location of the resource. | +| `type` | `IamResourceTypeEnum` | | +| `description` | `string` | | +| `created_at` | `string` | | +| `updated_at` | `string` | | + +### `IamRole` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `description` | `string` | | +| `type` | `RoleTypeEnum` | | +| `policies` | `array` | The set of policies associated with the role. | +| `created_at` | `string` | | +| `updated_at` | `string` | | + +### `IamUser` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `primary_email_address` | `string` | Primary email address of the user. This is generally a work email address. | +| `first_name` | `string` | | +| `last_name` | `string` | | +| `name` | `string` | User's name which (can be a full name or display name) | +| `username` | `string` | | +| `is_bot_user` | `object` | Indicates if the user is a bot or service user | +| `roles` | `array` | List of roles the user is assigned to | +| `groups` | `array` | List of groups the user is assigned to | +| `status` | `UserStatusEnum` | | +| `last_active_at` | `string` | The date this user was last active | +| `last_login_at` | `string` | The date this user last logged in | +| `created_at` | `string` | The date the user was created | +| `updated_at` | `string` | The date the user was created | +| `multi_factor_enabled` | `array` | The list of Multi-Factor Authentication (MFA) types enabled for the user. | +| `avatar` | `File` | The user's avatar data. This generally contains a URL within this property's 'contents' array. | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `FileFormatEnum` | `ez`, `aw`, `atom`, `atomcat`, `atomdeleted`, `atomsvc`, `dwd`, `held`, `rsat`, `bdoc`, `xcs`, `ccxml`, `cdfx`, `cdmia`, `cdmic`, `cdmid`, `cdmio`, `cdmiq`, `cu`, `mpd`, `davmount`, `dbk`, `dssc`, `xdssc`, `es`, `ecma`, `emma`, `emotionml`, `epub`, `exi`, `exp`, `fdt`, `pfr`, `geojson`, `gml`, `gpx`, `gxf`, `gz`, `hjson`, `stk`, `ink`, `inkml`, `ipfix`, `its`, `jar`, `war`, `ear`, `ser`, `class`, `js`, `mjs`, `json`, `map`, `json5`, `jsonml`, `jsonld`, `lgr`, `lostxml`, `hqx`, `cpt`, `mads`, `webmanifest`, `mrc`, `mrcx`, `ma`, `nb`, `mb`, `mathml`, `mbox`, `mscml`, `metalink`, `meta4`, `mets`, `maei`, `musd`, `mods`, `m21`, `mp21`, `mp4s`, `m4p`, `doc`, `dot`, `mxf`, `nq`, `nt`, `cjs`, `bin`, `dms`, `lrf`, `mar`, `so`, `dist`, `distz`, `pkg`, `bpk`, `dump`, `elc`, `deploy`, `exe`, `dll`, `deb`, `dmg`, `iso`, `img`, `msi`, `msp`, `msm`, `buffer`, `oda`, `opf`, `ogx`, `omdoc`, `onetoc`, `onetoc2`, `onetmp`, `onepkg`, `oxps`, `relo`, `xer`, `pdf`, `pgp`, `asc`, `sig`, `prf`, `p10`, `p7m`, `p7c`, `p7s`, `p8`, `ac`, `cer`, `crl`, `pkipath`, `pki`, `pls`, `ai`, `eps`, `ps`, `provx`, `pskcxml`, `raml`, `rdf`, `owl`, `rif`, `rnc`, `rl`, `rld`, `rs`, `rapd`, `sls`, `rusd`, `gbr`, `mft`, `roa`, `rsd`, `rss`, `rtf`, `sbml`, `scq`, `scs`, `spq`, `spp`, `sdp`, `senmlx`, `sensmlx`, `setpay`, `setreg`, `shf`, `siv`, `sieve`, `smi`, `smil`, `rq`, `srx`, `gram`, `grxml`, `sru`, `ssdl`, `ssml`, `swidtag`, `tei`, `teicorpus`, `tfi`, `tsd`, `toml`, `trig`, `ttml`, `ubj`, `rsheet`, `td`, `vxml`, `wasm`, `wgt`, `hlp`, `wsdl`, `wspolicy`, `xaml`, `xav`, `xca`, `xdf`, `xel`, `xns`, `xenc`, `xhtml`, `xht`, `xlf`, `xml`, `xsl`, `xsd`, `rng`, `dtd`, `xop`, `xpl`, `*xsl`, `xslt`, `xspf`, `mxml`, `xhvml`, `xvml`, `xvm`, `yang`, `yin`, `zip`, `*3gpp`, `adp`, `amr`, `au`, `snd`, `mid`, `midi`, `kar`, `rmi`, `mxmf`, `*mp3`, `m4a`, `mp4a`, `mpga`, `mp2`, `mp2a`, `mp3`, `m2a`, `m3a`, `oga`, `ogg`, `spx`, `opus`, `s3m`, `sil`, `wav`, `*wav`, `weba`, `xm`, `ttc`, `otf`, `ttf`, `woff`, `woff2`, `exr`, `apng`, `avif`, `bmp`, `cgm`, `drle`, `emf`, `fits`, `g3`, `gif`, `heic`, `heics`, `heif`, `heifs`, `hej2`, `hsj2`, `ief`, `jls`, `jp2`, `jpg2`, `jpeg`, `jpg`, `jpe`, `jph`, `jhc`, `jpm`, `jpx`, `jpf`, `jxr`, `jxra`, `jxrs`, `jxs`, `jxsc`, `jxsi`, `jxss`, `ktx`, `ktx2`, `png`, `sgi`, `svg`, `svgz`, `t38`, `tif`, `tiff`, `tfx`, `webp`, `wmf`, `disposition-notification`, `u8msg`, `u8dsn`, `u8mdn`, `u8hdr`, `eml`, `mime`, `3mf`, `gltf`, `glb`, `igs`, `iges`, `msh`, `mesh`, `silo`, `mtl`, `obj`, `stpx`, `stpz`, `stpxz`, `stl`, `wrl`, `vrml`, `*x3db`, `x3dbz`, `x3db`, `*x3dv`, `x3dvz`, `x3d`, `x3dz`, `x3dv`, `appcache`, `manifest`, `ics`, `ifb`, `coffee`, `litcoffee`, `css`, `csv`, `html`, `htm`, `shtml`, `jade`, `jsx`, `less`, `markdown`, `md`, `mml`, `mdx`, `n3`, `txt`, `text`, `conf`, `def`, `list`, `log`, `in`, `ini`, `rtx`, `*rtf`, `sgml`, `sgm`, `shex`, `slim`, `slm`, `spdx`, `stylus`, `styl`, `tsv`, `t`, `tr`, `roff`, `man`, `me`, `ms`, `ttl`, `uri`, `uris`, `urls`, `vcard`, `vtt`, `*xml`, `yaml`, `yml`, `3gp`, `3gpp`, `3g2`, `h261`, `h263`, `h264`, `m4s`, `jpgv`, `*jpm`, `jpgm`, `mj2`, `mjp2`, `ts`, `mp4`, `mp4v`, `mpg4`, `mpeg`, `mpg`, `mpe`, `m1v`, `m2v`, `ogv`, `qt`, `mov`, `webm`, `cww`, `1km`, `plb`, `psb`, `pvb`, `tcap`, `pwn`, `aso`, `imp`, `acu`, `atc`, `acutc`, `air`, `fcdt`, `fxp`, `fxpl`, `xdp`, `xfdf`, `ahead`, `azf`, `azs`, `azw`, `acc`, `ami`, `apk`, `cii`, `fti`, `atx`, `mpkg`, `key`, `m3u8`, `numbers`, `pages`, `pkpass`, `swi`, `iota`, `aep`, `bmml`, `mpm`, `bmi`, `rep`, `cdxml`, `mmd`, `cdy`, `csl`, `cla`, `rp9`, `c4g`, `c4d`, `c4f`, `c4p`, `c4u`, `c11amc`, `c11amz`, `csp`, `cdbcmsg`, `cmc`, `clkx`, `clkk`, `clkp`, `clkt`, `clkw`, `wbs`, `pml`, `ppd`, `car`, `pcurl`, `dart`, `rdz`, `dbf`, `uvf`, `uvvf`, `uvd`, `uvvd`, `uvt`, `uvvt`, `uvx`, `uvvx`, `uvz`, `uvvz`, `fe_launch`, `dna`, `mlp`, `mle`, `dpg`, `dfac`, `kpxx`, `ait`, `svc`, `geo`, `mag`, `nml`, `esf`, `msf`, `qam`, `slt`, `ssf`, `es3`, `et3`, `ez2`, `ez3`, `fdf`, `mseed`, `seed`, `dataless`, `gph`, `ftc`, `fm`, `frame`, `maker`, `book`, `fnc`, `ltf`, `fsc`, `oas`, `oa2`, `oa3`, `fg5`, `bh2`, `ddd`, `xdw`, `xbd`, `fzs`, `txd`, `ggb`, `ggt`, `gex`, `gre`, `gxt`, `g2w`, `g3w`, `gmx`, `gdoc`, `gslides`, `gsheet`, `kml`, `kmz`, `gqf`, `gqs`, `gac`, `ghf`, `gim`, `grv`, `gtm`, `tpl`, `vcg`, `hal`, `zmm`, `hbci`, `les`, `hpgl`, `hpid`, `hps`, `jlt`, `pcl`, `pclxl`, `sfd-hdstx`, `mpy`, `afp`, `listafp`, `list3820`, `irm`, `sc`, `icc`, `icm`, `igl`, `ivp`, `ivu`, `igm`, `xpw`, `xpx`, `i2g`, `qbo`, `qfx`, `rcprofile`, `irp`, `xpr`, `fcs`, `jam`, `rms`, `jisp`, `joda`, `ktz`, `ktr`, `karbon`, `chrt`, `kfo`, `flw`, `kon`, `kpr`, `kpt`, `ksp`, `kwd`, `kwt`, `htke`, `kia`, `kne`, `knp`, `skp`, `skd`, `skt`, `skm`, `sse`, `lasxml`, `lbd`, `lbe`, `apr`, `pre`, `nsf`, `org`, `scm`, `lwp`, `portpkg`, `mvt`, `mcd`, `mc1`, `cdkey`, `mwf`, `mfm`, `flo`, `igx`, `mif`, `daf`, `dis`, `mbk`, `mqy`, `msl`, `plc`, `txf`, `mpn`, `mpc`, `xul`, `cil`, `cab`, `xls`, `xlm`, `xla`, `xlc`, `xlt`, `xlw`, `xlam`, `xlsb`, `xlsm`, `xltm`, `eot`, `chm`, `ims`, `lrm`, `thmx`, `msg`, `cat`, `*stl`, `ppt`, `pps`, `pot`, `ppam`, `pptm`, `sldm`, `ppsm`, `potm`, `mpp`, `mpt`, `docm`, `dotm`, `wps`, `wks`, `wcm`, `wdb`, `wpl`, `xps`, `mseq`, `mus`, `msty`, `taglet`, `nlu`, `ntf`, `nitf`, `nnd`, `nns`, `nnw`, `*ac`, `ngdat`, `n-gage`, `rpst`, `rpss`, `edm`, `edx`, `ext`, `odc`, `otc`, `odb`, `odf`, `odft`, `odg`, `otg`, `odi`, `oti`, `odp`, `otp`, `ods`, `ots`, `odt`, `odm`, `ott`, `oth`, `xo`, `dd2`, `obgx`, `oxt`, `osm`, `pptx`, `sldx`, `ppsx`, `potx`, `xlsx`, `xltx`, `docx`, `dotx`, `mgp`, `dp`, `esa`, `pdb`, `pqa`, `oprc`, `paw`, `str`, `ei6`, `efif`, `wg`, `plf`, `pbd`, `box`, `mgz`, `qps`, `ptid`, `qxd`, `qxt`, `qwd`, `qwt`, `qxl`, `qxb`, `rar`, `bed`, `mxl`, `musicxml`, `cryptonote`, `cod`, `rm`, `rmvb`, `link66`, `st`, `see`, `sema`, `semd`, `semf`, `ifm`, `itp`, `iif`, `ipk`, `twd`, `twds`, `mmf`, `teacher`, `fo`, `sdkm`, `sdkd`, `dxp`, `sfs`, `sdc`, `sda`, `sdd`, `smf`, `sdw`, `vor`, `sgl`, `smzip`, `sm`, `wadl`, `sxc`, `stc`, `sxd`, `std`, `sxi`, `sti`, `sxm`, `sxw`, `sxg`, `stw`, `sus`, `susp`, `svd`, `sis`, `sisx`, `xsm`, `bdm`, `xdm`, `ddf`, `tao`, `pcap`, `cap`, `dmp`, `tmo`, `tpt`, `mxs`, `tra`, `ufd`, `ufdl`, `utz`, `umj`, `unityweb`, `uoml`, `vcx`, `vsd`, `vst`, `vss`, `vsw`, `vis`, `vsf`, `wbxml`, `wmlc`, `wmlsc`, `wtb`, `nbp`, `wpd`, `wqd`, `stf`, `xar`, `xfdl`, `hvd`, `hvs`, `hvp`, `osf`, `osfpvg`, `saf`, `spf`, `cmp`, `zir`, `zirz`, `zaz`, `7z`, `abw`, `ace`, `*dmg`, `arj`, `aab`, `x32`, `u32`, `vox`, `aam`, `aas`, `bcpio`, `*bdoc`, `torrent`, `blb`, `blorb`, `bz`, `bz2`, `boz`, `cbr`, `cba`, `cbt`, `cbz`, `cb7`, `vcd`, `cfs`, `chat`, `pgn`, `crx`, `cco`, `nsc`, `cpio`, `csh`, `*deb`, `udeb`, `dgc`, `dir`, `dcr`, `dxr`, `cst`, `cct`, `cxt`, `w3d`, `fgd`, `swa`, `wad`, `ncx`, `dtb`, `res`, `dvi`, `evy`, `eva`, `bdf`, `gsf`, `psf`, `pcf`, `snf`, `pfa`, `pfb`, `pfm`, `afm`, `arc`, `spl`, `gca`, `ulx`, `gnumeric`, `gramps`, `gtar`, `hdf`, `php`, `install`, `*iso`, `*key`, `*numbers`, `*pages`, `jardiff`, `jnlp`, `kdbx`, `latex`, `luac`, `lzh`, `lha`, `run`, `mie`, `prc`, `mobi`, `application`, `lnk`, `wmd`, `wmz`, `xbap`, `mdb`, `obd`, `crd`, `clp`, `*exe`, `*dll`, `com`, `bat`, `*msi`, `mvb`, `m13`, `m14`, `*wmf`, `*wmz`, `*emf`, `emz`, `mny`, `pub`, `scd`, `trm`, `wri`, `nc`, `cdf`, `pac`, `nzb`, `pl`, `pm`, `*prc`, `*pdb`, `p12`, `pfx`, `p7b`, `spc`, `p7r`, `*rar`, `rpm`, `ris`, `sea`, `sh`, `shar`, `swf`, `xap`, `sql`, `sit`, `sitx`, `srt`, `sv4cpio`, `sv4crc`, `t3`, `gam`, `tar`, `tcl`, `tk`, `tex`, `tfm`, `texinfo`, `texi`, `*obj`, `ustar`, `hdd`, `ova`, `ovf`, `vbox`, `vbox-extpack`, `vdi`, `vhd`, `vmdk`, `src`, `webapp`, `der`, `crt`, `pem`, `fig`, `*xlf`, `xpi`, `xz`, `z1`, `z2`, `z3`, `z4`, `z5`, `z6`, `z7`, `z8`, `uva`, `uvva`, `eol`, `dra`, `dts`, `dtshd`, `lvp`, `pya`, `ecelp4800`, `ecelp7470`, `ecelp9600`, `rip`, `aac`, `aif`, `aiff`, `aifc`, `caf`, `flac`, `*m4a`, `mka`, `m3u`, `wax`, `wma`, `ram`, `ra`, `rmp`, `*ra`, `cdx`, `cif`, `cmdf`, `cml`, `csml`, `xyz`, `btif`, `pti`, `psd`, `azv`, `uvi`, `uvvi`, `uvg`, `uvvg`, `djvu`, `djv`, `*sub`, `dwg`, `dxf`, `fbs`, `fpx`, `fst`, `mmr`, `rlc`, `ico`, `dds`, `mdi`, `wdp`, `npx`, `b16`, `tap`, `vtf`, `wbmp`, `xif`, `pcx`, `3ds`, `ras`, `cmx`, `fh`, `fhc`, `fh4`, `fh5`, `fh7`, `*ico`, `jng`, `sid`, `*bmp`, `*pcx`, `pic`, `pct`, `pnm`, `pbm`, `pgm`, `ppm`, `rgb`, `tga`, `xbm`, `xpm`, `xwd`, `wsc`, `dae`, `dwf`, `gdl`, `gtw`, `mts`, `ogex`, `x_b`, `x_t`, `vds`, `usdz`, `bsp`, `vtu`, `dsc`, `curl`, `dcurl`, `mcurl`, `scurl`, `sub`, `fly`, `flx`, `gv`, `3dml`, `spot`, `jad`, `wml`, `wmls`, `s`, `asm`, `c`, `cc`, `cxx`, `cpp`, `h`, `hh`, `dic`, `htc`, `f`, `for`, `f77`, `f90`, `hbs`, `java`, `lua`, `mkd`, `nfo`, `opml`, `*org`, `p`, `pas`, `pde`, `sass`, `scss`, `etx`, `sfv`, `ymp`, `uu`, `vcs`, `vcf`, `uvh`, `uvvh`, `uvm`, `uvvm`, `uvp`, `uvvp`, `uvs`, `uvvs`, `uvv`, `uvvv`, `dvb`, `fvt`, `mxu`, `m4u`, `pyv`, `uvu`, `uvvu`, `viv`, `f4v`, `fli`, `flv`, `m4v`, `mkv`, `mk3d`, `mks`, `mng`, `asf`, `asx`, `vob`, `wm`, `wmv`, `wmx`, `wvx`, `avi`, `movie`, `smv`, `ice`, `mht` | +| `GroupTypeEnum` | `workspace`, `team`, `department`, `group`, `organization`, `cost_center` | +| `IamMfaTypeEnum` | `sms`, `email`, `push`, `totp`, `phone_call`, `question`, `software_token`, `hardware_token`, `web`, `unknown` | +| `IamPermissionTypeEnum` | `read`, `read_write`, `approve`, `delete`, `use`, `export` | +| `IamResourceTypeEnum` | `project`, `file`, `folder`, `product`, `property`, `user` | +| `RoleTypeEnum` | `admin`, `viewer`, `editor`, `basic`, `guest`, `unassigned`, `restricted` | +| `UserStatusEnum` | `enabled`, `disabled`, `pending` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/lms.md b/.claude/plugins/stackone-connector-builder/references/schemas/lms.md new file mode 100644 index 0000000..1133e8d --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/lms.md @@ -0,0 +1,264 @@ +# LMS (Learning Management System) — Schema Reference + +> Manage courses, content, enrollments and completions. + +> **OAS source:** `https://docs.stackone.com/lms/api-reference/lms.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/lms/courses` | List Courses | +| `GET` | `/unified/lms/courses/{id}` | Get Course | +| `GET` | `/unified/lms/users/{id}/assignments` | List User Assignments | +| `POST` | `/unified/lms/users/{id}/assignments` | Create User Assignment | +| `GET` | `/unified/lms/users/{id}/assignments/{subResourceId}` | Get User Assignment | +| `POST` | `/unified/lms/content/batch` | Batch Upsert External Linking Learning Objects | +| `PUT` | `/unified/lms/content` | Upsert External Linking Learning Objects | +| `GET` | `/unified/lms/content` | List Content | +| `GET` | `/unified/lms/content/{id}` | Get Content | +| `GET` | `/unified/lms/users/{id}/completions` | List User Completions | +| `POST` | `/unified/lms/users/{id}/completions` | Create User Completion | +| `GET` | `/unified/lms/users/{id}/completions/{subResourceId}` | Get User Completion | +| `DELETE` | `/unified/lms/users/{id}/completions/{subResourceId}` | Delete User Completion | +| `GET` | `/unified/lms/completions` | List Completions | +| `GET` | `/unified/lms/completions/{id}` | Get Completion | +| `GET` | `/unified/lms/categories/{id}` | Get Category | +| `GET` | `/unified/lms/categories` | List Categories | +| `GET` | `/unified/lms/users` | List Users | +| `GET` | `/unified/lms/users/{id}` | Get User | +| `GET` | `/unified/lms/skills/{id}` | Get Skill | +| `GET` | `/unified/lms/skills` | List Skills | +| `GET` | `/unified/lms/assignments` | List Assignments | +| `GET` | `/unified/lms/assignments/{id}` | Get Assignment | + +--- + +## Models + +### `AdditionalData` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The name of the additional data field. Speak to your Solutions Engineer to understand the id for ... | +| `remote_id` | `string` | Provider's unique identifier | +| `value` | `object` | The value of the additional data | + +### `Assignment` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this assignment | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `external_reference` | `string` | The external reference associated with this assignment | +| `learning_object_id` | `string` | The learning_object_id associated with this assignment. This is not required unless specified in ... | +| `remote_learning_object_id` | `string` | Provider's unique identifier of the learning object related to the assignment | +| `learning_object_external_reference` | `string` | The external reference of the learning object associated with this assignment, this is the main i... | +| `progress` | `number` | The progress associated with this assigment | +| `updated_at` | `string` | The date the assignment was last updated | +| `created_at` | `string` | The date the assignment was created | +| `assigned_at` | `string` | The date the assignment was assigned | +| `due_date` | `string` | The date the assignment is due to be completed | +| `status` | `AssignmentStatusEnum` | The status of the assignment | +| `learning_object_type` | `LearningObjectTypeEnum` | The learning object type of the assignment | +| `user_id` | `string` | The user ID associated with this assignment | +| `remote_user_id` | `string` | Provider's unique identifier of the user related to the assignment | +| `certificate_url` | `string` | The certification URL associated with this assignment | +| `result` | `ResultStatusEnum` | The result of the assignment | +| `completed_at` | `string` | The date the content was completed | +| `course_id` | `string` | The course ID associated with this assignment | +| `remote_course_id` | `string` | Provider's unique identifier of the course related to the assignment | + +### `AuthorModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID of the author | +| `name` | `string` | The name of the author | + +### `Category` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this category | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name associated with this category | +| `active` | `object` | Whether the category is active and therefore available for use | +| `hierarchy` | `CategoryLevelEnumModel` | The hierarchal level of the category | +| `level` | `CategoryLevelEnumModel` | The hierarchal level of the category | +| `language` | `LanguageEnum` | The language associated with this category | + +### `Completion` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this completion | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `learning_object_external_reference` | `string` | The external reference of the learning object associated with this completion, this is the main i... | +| `external_reference` | `string` | The external reference associated with this completion | +| `result` | `ResultStatusEnum` | The result of the completion | +| `completed_at` | `string` | The date the content was completed | +| `created_at` | `string` | The created date of the completion | +| `updated_at` | `string` | The updated date of the completion | +| `learning_object_type` | `LearningObjectTypeEnum` | The learning object type of the completion | +| `learning_object_id` | `string` | The id of the learning object associated with this completion. This is not required unless specif... | +| `remote_learning_object_id` | `string` | Provider's unique identifier of the learning object related to the completion | +| `user_id` | `string` | The user ID associated with this completion | +| `remote_user_id` | `string` | Provider's unique identifier of the user related to the completion | +| `time_spent` | `string` | ISO 8601 duration format representing the time spent on completing the learning object | +| `certificate_url` | `string` | The certification URL associated with this completion | +| `external_id` | `string` | The external ID associated with this completion | +| `content_external_reference` | `string` | The external reference associated with this content | +| `remote_external_id` | `string` | Provider's unique identifier of the content external reference | +| `content_id` | `string` | The content ID associated with this completion | +| `remote_content_id` | `string` | Provider's unique identifier of the content associated with the completion | +| `course_id` | `string` | The course ID associated with this completion | +| `remote_course_id` | `string` | Provider's unique identifier of the course associated with the completion | +| `score` | `ScoreModel` | The score associated with this completion | + +### `Content` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `external_reference` | `string` | The external ID associated with this content | +| `course_ids` | `array` | The parent ID/IDs associated with this content | +| `remote_course_ids` | `array` | Provider's unique identifier of the parent course ID associated with this content | +| `title` | `string` | The title of the content | +| `description` | `string` | The description of the content | +| `languages` | `array` | The languages associated with this content | +| `content_url` | `string` | The external URL of the content | +| `mobile_launch_content_url` | `string` | The mobile friendly URL of the content | +| `content_type` | `ContentTypeEnum` | The type of content | +| `cover_url` | `string` | The URL of the thumbnail image associated with the content. | +| `active` | `object` | Whether the content is active and available for users. | +| `duration` | `string` | The duration of the content following the ISO8601 standard. If duration_unit is applicable we wil... | +| `categories` | `array` | The categories associated with this content | +| `skills` | `array` | The skills associated with this course | +| `order` | `number` | The order of the individual content within a content grouping. This is not applicable for pushing... | +| `provider` | `string` | The name of the content provider | +| `short_description` | `string` | A short description or summary for the content | +| `localizations` | `array` | Localised content information | +| `tags` | `array` | A list of tags associated with the content | +| `authors` | `array` | The authors of the content | +| `updated_at` | `string` | The date on which the content was last updated. | +| `created_at` | `string` | The date on which the content was created. | + +### `Course` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `external_reference` | `string` | The external ID associated with this course | +| `content_ids` | `array` | The child ID/IDs associated with this course | +| `remote_content_ids` | `array` | Provider's unique identifier of the child content IDs associated with this course | +| `title` | `string` | The title of the course | +| `description` | `string` | The description of the course | +| `languages` | `array` | The languages associated with this course | +| `cover_url` | `string` | The URL of the thumbnail image associated with the course. | +| `url` | `string` | The redirect URL of the course. | +| `active` | `object` | Whether the course is active and available for users. | +| `duration` | `string` | The duration of the course following the ISO8601 standard. If duration_unit is applicable we will... | +| `categories` | `array` | The categories associated with this course | +| `skills` | `array` | The skills associated with this course | +| `provider` | `string` | The name of the course provider | +| `authors` | `array` | The authors of the course | +| `updated_at` | `string` | The date on which the course was last updated. | +| `created_at` | `string` | The date on which the course was created. | + +### `CreateCategoriesApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this category | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `name` | `string` | The name associated with this category | +| `hierarchy` | `CategoryLevelEnumModel` | The hierarchal level of the category | +| `level` | `CategoryLevelEnumModel` | The hierarchal level of the category | +| `language` | `LanguageEnum` | The language associated with this category | + +### `CreateSkillsApiModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this skill | +| `name` | `string` | The name associated with this skill | +| `hierarchy` | `SkillLevelEnum` | The hierarchal level of the skill | +| `language` | `LanguageEnum` | The language associated with this skill | +| `level` | `SkillLevelEnum` | The hierarchal level of the skill | + +### `LmsUser` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `external_reference` | `string` | The external ID associated with this user | +| `name` | `string` | The user name | +| `email` | `string` | The user email | +| `phone_number` | `string` | The user phone number | +| `active` | `object` | The user active status | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | + +### `LocalizationModel` +| Field | Type | Description | +|-------|------|-------------| +| `title` | `string` | The title of the content | +| `description` | `string` | The description of the content | +| `language` | `LanguageEnum` | The language associated with the localization details | +| `tags` | `array` | The tags associated with the localization details | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `ScoreModel` +| Field | Type | Description | +|-------|------|-------------| +| `percentage` | `number` | The score percentage | +| `raw_value` | `string` | The raw string score value | + +### `Skills` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The ID associated with this skill | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name associated with this skill | +| `active` | `object` | Whether the skill is active and therefore available for use | +| `hierarchy` | `SkillLevelEnum` | The hierarchal level of the skill | +| `language` | `LanguageEnum` | The language associated with this skill | +| `level` | `SkillLevelEnum` | The hierarchal level of the skill | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +### `UpsertResultDataExternalReferenceModel` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `external_reference` | `string` | The external identifier | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `AssignmentStatusEnum` | `pending`, `in_progress`, `completed` | +| `CategoryLevelEnumModel` | `primary`, `secondary`, `tertiary` | +| `ContentTypeEnum` | `video`, `quiz`, `document`, `audio`, `article` | +| `LanguageEnum` | `ar_AR`, `aa_ER`, `af_NA`, `af_ZA`, `am_ET`, `ar_AE`, `ar_BH`, `ar_DJ`, `ar_DZ`, `ar_EG`, `ar_ER`, `ar_IQ`, `ar_JO`, `ar_KM`, `ar_KW`, `ar_LB`, `ar_LY`, `ar_MA`, `ar_MR`, `ar_OM`, `ar_PS`, `ar_QA`, `ar_SA`, `ar_SD`, `ar_SY`, `ar_TD`, `ar_TN`, `ar_YE`, `ay_BO`, `ay_PE`, `az_AZ`, `az_IR`, `be_BY`, `bg_BG`, `bi_VU`, `bn_BD`, `bn_IN`, `bs_BA`, `bs-ME`, `byn_ER`, `ca_AD`, `ca_ES`, `ca_FR`, `ca_IT`, `ch_GU`, `cs_CZ`, `da_DK`, `de_AT`, `de_BE`, `de_CH`, `de_DE`, `de_LI`, `de_LU`, `de_VA`, `de_MV`, `dv_MV`, `dz_BT`, `el_CY`, `el_GR`, `en_AG`, `en_AI`, `en_AS`, `en_AU`, `en_BB`, `en_BE`, `en_BM`, `en_BS`, `en_BW`, `en_BZ`, `en_CA`, `en_CC`, `en_CK`, `en_CM`, `en_CW`, `en_CX`, `en_DG`, `en_DM`, `en_ER`, `en_FJ`, `en_FK`, `en_FM`, `en_GB`, `en_GD`, `en_GG`, `en_GH`, `en_GI`, `en_GM`, `en_GS`, `en_GU`, `en_GY`, `en_HK`, `en_IE`, `en_IM`, `en_IN`, `en_IO`, `en_JE`, `en_JM`, `en_KE`, `en_KI`, `en_KN`, `en_KY`, `en_LC`, `en_LR`, `en_LS`, `en_MF`, `en_MG`, `en_MH`, `en_MO`, `en_MP`, `en_MS`, `en_MT`, `en_MU`, `en_MW`, `en_MY`, `en_NA`, `en_NF`, `en_NG`, `en_NL`, `en_NR`, `en_NU`, `en_NZ`, `en_PG`, `en_PH`, `en_PK`, `en_PN`, `en_PR`, `en_PW`, `en_RW`, `en_SB`, `en_SC`, `en_SD`, `en_SG`, `en_SH`, `en_SL`, `en_SS`, `en_SX`, `en_SZ`, `en_TC`, `en_TK`, `en_TO`, `en_TT`, `en_TV`, `en_TZ`, `en_UG`, `en_UM`, `en_US`, `en_VC`, `en_VG`, `en_VI`, `en_VU`, `en_WS`, `en_ZA`, `en_ZM`, `en_ZW`, `es_AR`, `es_BO`, `es_BZ`, `es_CL`, `es_CO`, `es_CR`, `es_CU`, `es_DO`, `es_EA`, `es_EC`, `es_EH`, `es_ES`, `es_GQ`, `es_GT`, `es_HN`, `es_IC`, `es_LA`, `es_MX`, `es_NI`, `es_PA`, `es_PE`, `es_PH`, `es_PR`, `es_PY`, `es_SV`, `es_US`, `es_UY`, `es_VE`, `et_EE`, `fa_AF`, `fa_IR`, `fan_GA`, `ff_CM`, `ff_GN`, `ff_MR`, `ff_SN`, `ff_BF`, `fi_FI`, `fj_FJ`, `fo_FO`, `fr_BE`, `fr_BF`, `fr_BI`, `fr_BJ`, `fr_BL`, `fr_CA`, `fr_CD`, `fr_CF`, `fr_CG`, `fr_CH`, `fr_CI`, `fr_CM`, `fr_DJ`, `fr_DZ`, `fr_FR`, `fr_GA`, `fr_GF`, `fr_GG`, `fr_GN`, `fr_GP`, `fr_GQ`, `fr_HT`, `fr_KM`, `fr_JE`, `fr_LU`, `fr_LB`, `fr_MA`, `fr_MC`, `fr_MF`, `fr_MG`, `fr_ML`, `fr_MQ`, `fr_MR`, `fr_MU`, `fr_NC`, `fr_NE`, `fr_PF`, `fr_PM`, `fr_RE`, `fr_RW`, `fr_SC`, `fr_SN`, `fr_SY`, `fr_TD`, `fr_TF`, `fr_TG`, `fr_TN`, `fr_VU`, `fr_VA`, `fr_WF`, `fr_YT`, `ga_IE`, `gn_PY`, `gn_AR`, `gu_IN`, `gv_IM`, `he_IL`, `hi_IN`, `hr_BA`, `hr_HR`, `hr_ME`, `ht_HT`, `hu_HU`, `hy_AM`, `hy_CY`, `id_ID`, `is_IS`, `it_CH`, `it_IT`, `it_SM`, `it_VA`, `ja_JP`, `ka_GE`, `kg_CD`, `kk_KZ`, `kl_GL`, `km_KH`, `ko_KP`, `ko_KR`, `ku_IQ`, `ky_KG`, `la_VA`, `lb_LU`, `ln_AO`, `ln_CD`, `ln_CF`, `ln_CG`, `lo_LA`, `lt_LT`, `lu_CD`, `lv_LV`, `mg_MG`, `mh_MH`, `mi_NZ`, `mk_MK`, `mn_MN`, `mr_IN`, `ms_BN`, `ms_MY`, `ms_SG`, `mt_MT`, `my_MM`, `nb_NO`, `nb_BV`, `nb_ZW`, `ne_NP`, `nl_AW`, `nl_BE`, `nl_BQ`, `nl_CW`, `nl_NL`, `nl_SR`, `nl_SX`, `nl_MF`, `nn_NO`, `nn_BV`, `no_NO`, `no_BV`, `no_SJ`, `nr_ZA`, `ny_MW`, `pa_IN`, `pa_PK`, `pl_PL`, `ps_AF`, `pt_AO`, `pt_BR`, `pt_CH`, `pt_CV`, `pt_GQ`, `pt_GW`, `pt_LU`, `pt_MO`, `pt_MZ`, `pt_PT`, `pt_ST`, `pt_TL`, `qu_BO`, `qu_EC`, `qu_PE`, `rar_CK`, `rm_CH`, `rup_MK`, `ro_MD`, `ro_RO`, `ru_BY`, `ru_KG`, `ru_KZ`, `ru_MD`, `ru_RU`, `ru_UA`, `ru_AQ`, `ru_TJ`, `ru_TM`, `ru_UZ`, `rw_RW`, `se_SE`, `sg_CF`, `si_LK`, `sk_SK`, `sl_SI`, `sm_AS`, `sm_WS`, `sn_ZW`, `so_DJ`, `so_ET`, `so_KE`, `so_SO`, `sq_AL`, `sq_ME`, `sq_XK`, `sr_BA`, `sr_ME`, `sr_RS`, `sr_XK`, `ss_SZ`, `ss_ZA`, `sv_AX`, `sv_FI`, `sv_SE`, `sw_KE`, `sw_TZ`, `sw_UG`, `sw_CD`, `ta_IN`, `ta_MY`, `ta_SG`, `ta_LK`, `te_IN`, `tg_TJ`, `th_TH`, `ti_ER`, `ti_ET`, `tig_ER`, `tk_TM`, `tk_AF`, `tn_BW`, `tn_ZA`, `to_TO`, `tr_CY`, `tr_TR`, `ts_ZA`, `uk_UA`, `ur_IN`, `ur_PK`, `uz_AF`, `uz_UZ`, `ve_ZA`, `vi_VN`, `xh_ZA`, `zh_CN`, `zh_HK`, `zh_MO`, `zh_SG`, `zh_TW`, `zu_ZA` | +| `LearningObjectTypeEnum` | `content`, `course`, `collection` | +| `ResultStatusEnum` | `Pass`, `Fail` | +| `SkillLevelEnum` | `primary`, `secondary`, `tertiary` | +| `WriteContentTypeEnum` | `video`, `quiz`, `document`, `audio`, `article`, `book`, `event`, `course`, `collection`, `interactive` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/marketing.md b/.claude/plugins/stackone-connector-builder/references/schemas/marketing.md new file mode 100644 index 0000000..f605646 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/marketing.md @@ -0,0 +1,236 @@ +# Marketing Automation — Schema Reference + +> Manage campaigns, email templates, contacts and push/SMS. + +> **OAS source:** `https://docs.stackone.com/marketing/api-reference/marketing.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/marketing/templates/email` | List Email Templates | +| `POST` | `/unified/marketing/templates/email` | Create Email Templates | +| `GET` | `/unified/marketing/templates/email/{id}` | Get Email Templates | +| `PATCH` | `/unified/marketing/templates/email/{id}` | Update Email Templates | +| `GET` | `/unified/marketing/templates/in_app` | List In-App Templates | +| `POST` | `/unified/marketing/templates/in_app` | Create In-App Template | +| `GET` | `/unified/marketing/templates/in_app/{id}` | Get In-App Template | +| `PATCH` | `/unified/marketing/templates/in_app/{id}` | Update In-App Template | +| `GET` | `/unified/marketing/templates/sms` | List SMS Templates | +| `POST` | `/unified/marketing/templates/sms` | Create SMS Template | +| `GET` | `/unified/marketing/templates/sms/{id}` | Get SMS Template | +| `PATCH` | `/unified/marketing/templates/sms/{id}` | Update SMS Template | +| `GET` | `/unified/marketing/templates/omni_channel` | List Omni-Channel Templates | +| `POST` | `/unified/marketing/templates/omni_channel` | Create Omni-Channel Template | +| `GET` | `/unified/marketing/templates/omni_channel/{id}` | Get Omni-Channel Template | +| `PATCH` | `/unified/marketing/templates/omni_channel/{id}` | Update Omni-Channel Template | +| `GET` | `/unified/marketing/templates/push` | List Push Templates | +| `POST` | `/unified/marketing/templates/push` | Create Push Template | +| `GET` | `/unified/marketing/templates/push/{id}` | Get Push Template | +| `PATCH` | `/unified/marketing/templates/push/{id}` | Update Push Template | +| `GET` | `/unified/marketing/campaigns` | List campaigns | +| `GET` | `/unified/marketing/campaigns/{id}` | Get campaign | +| `GET` | `/unified/marketing/content_blocks` | List Content Blocks | +| `POST` | `/unified/marketing/content_blocks` | Create Content Block | +| `GET` | `/unified/marketing/content_blocks/{id}` | Get Content Blocks | +| `PATCH` | `/unified/marketing/content_blocks/{id}` | Update Content Block | + +--- + +## Models + +### `Campaign` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `created_at` | `string` | The created_at date | +| `updated_at` | `string` | The updated_at date | +| `description` | `string` | | +| `schedule_type` | `ScheduleTypeEnum` | The schedule type | +| `status` | `StatusEnum` | Status of the Campaign | +| `archived` | `object` | | +| `draft` | `object` | | +| `channels` | `array` | channels of the Campaign | +| `first_sent_at` | `string` | The first_sent_at date | +| `last_sent_at` | `string` | The last_sent_at date | +| `tags` | `array` | | +| `messages` | `array` | | + +### `ContentBlock` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `content` | `string` | | +| `type` | `ContentBlockTypeEnumApiModel` | Stackone enum identifying the type of content block. | +| `status` | `ContentBlockStatusEnumApiModel` | Stackone enum identifying the status of content block. | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `CreateMessage` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | Stackone enum identifying the type of message associated with the content. | +| `message_content` | `object` | | + +### `EmailMessageContents` +| Field | Type | Description | +|-------|------|-------------| +| `subject` | `string` | | +| `body` | `string` | | +| `from` | `string` | | +| `reply-to` | `string` | | +| `preheader` | `string` | | + +### `EmailMessages` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | | +| `message_content` | `EmailMessageContents` | | + +### `EmailTemplate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `messages` | `array` | | + +### `InAppMessageContents` +| Field | Type | Description | +|-------|------|-------------| +| `body` | `string` | | + +### `InAppMessages` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | | +| `message_content` | `InAppMessageContents` | | + +### `InAppTemplate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `messages` | `array` | | + +### `Message` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | Stackone enum identifying the type of message associated with the content. | +| `message_content` | `object` | | + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `PushMessageContents` +| Field | Type | Description | +|-------|------|-------------| +| `title` | `string` | | +| `subtitle` | `string` | | +| `body` | `string` | | + +### `PushMessages` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | | +| `message_content` | `PushMessageContents` | | + +### `PushTemplate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `messages` | `array` | | + +### `SmsMessageContents` +| Field | Type | Description | +|-------|------|-------------| +| `body` | `string` | | +| `from` | `string` | | + +### `SmsMessages` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `message_type` | `MessageTypeEnum` | | +| `message_content` | `SmsMessageContents` | | + +### `SmsTemplate` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | +| `messages` | `array` | | + +### `Template` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | | +| `tags` | `array` | | +| `created_at` | `string` | Date of creation | +| `updated_at` | `string` | Date of last update | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `ChannelsEnum` | `email`, `sms`, `web_push`, `ios_push`, `android_push`, `unknown` | +| `ContentBlockStatusEnumApiModel` | `draft`, `live`, `archived` | +| `ContentBlockTypeEnumApiModel` | `text`, `html`, `image`, `code-snippet` | +| `MessageTypeEnum` | `email`, `sms`, `push`, `web_push`, `ios_push`, `android_push`, `app_push`, `omni_channel`, `content_block`, `in_app`, `unknown` | +| `ScheduleTypeEnum` | `immediate`, `scheduled`, `recurring`, `custom`, `triggered` | +| `StatusEnum` | `draft`, `archived`, `live` | diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/ticketing.md b/.claude/plugins/stackone-connector-builder/references/schemas/ticketing.md new file mode 100644 index 0000000..f23fd17 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/schemas/ticketing.md @@ -0,0 +1,180 @@ +# Ticketing — Schema Reference + +> Manage tickets, projects, collections and users. + +> **OAS source:** `https://docs.stackone.com/ticketing/api-reference/ticketing.json` + +--- + +## Endpoints + +| Method | Path | Summary | +|--------|------|---------| +| `GET` | `/unified/ticketing/tickets` | List Tickets | +| `POST` | `/unified/ticketing/tickets` | Create Ticket | +| `GET` | `/unified/ticketing/tickets/{id}` | Get Ticket | +| `PATCH` | `/unified/ticketing/tickets/{id}` | Update Ticket | +| `GET` | `/unified/ticketing/users` | List Users | +| `GET` | `/unified/ticketing/users/{id}` | Get User | +| `GET` | `/unified/ticketing/tickets/{id}/comments` | List Comments | +| `GET` | `/unified/ticketing/tickets/{id}/comments/{subResourceId}` | Get Comment | +| `GET` | `/unified/ticketing/tickets/{id}/attachments/{subResourceId}/download` | Download Attachment | +| `GET` | `/unified/ticketing/tickets/{id}/attachments` | List Attachments | +| `GET` | `/unified/ticketing/tickets/{id}/attachments/{subResourceId}` | Get Attachment | +| `GET` | `/unified/ticketing/ticket_types` | List Ticket Types | +| `GET` | `/unified/ticketing/ticket_types/{id}` | Get Ticket Type | +| `GET` | `/unified/ticketing/projects` | List Projects | +| `GET` | `/unified/ticketing/projects/{id}` | Get Project | +| `GET` | `/unified/ticketing/projects/{id}/components` | List Project Components | +| `GET` | `/unified/ticketing/projects/{id}/components/{subResourceId}` | Get Project Component | +| `GET` | `/unified/ticketing/projects/{id}/ticket_types` | List Project Ticket Types | +| `GET` | `/unified/ticketing/tickets/{id}/statuses` | List Ticket Statuses | + +--- + +## Models + +### `ProviderError` +| Field | Type | Description | +|-------|------|-------------| +| `status` | `number` | HTTP status code of the provider error | +| `url` | `string` | URL that caused the error | +| `raw` | `object` | Raw error response from the provider | +| `headers` | `object` | Response headers | + +### `TicketingAttachment` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `ticket_id` | `string` | The reference ticket ID the attachment belongs to | +| `user_id` | `string` | The user who uploaded the file | +| `file_name` | `string` | The name of the file | +| `file_format` | `FileFormatEnum` | The type of the file | +| `file_url` | `string` | The resource URL of the file | +| `size` | `number` | The size of the file | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `TicketingComment` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `ticket_id` | `string` | The ticket ID associated with the comment | +| `user_id` | `string` | The user who created the comment | +| `internal` | `object` | Whether the comment is internal | +| `content` | `array` | Array of content associated with the comment | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `TicketingComponent` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `organization_id` | `string` | The organization id related to the component | +| `project_id` | `string` | The project id related to the component | +| `name` | `string` | The name of the component | +| `description` | `string` | The description of the component | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `TicketingContent` +| Field | Type | Description | +|-------|------|-------------| +| `plain` | `string` | The content of the ticket | +| `html` | `string` | The HTML content of the ticket | + +### `TicketingOrganization` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `domain` | `string` | The domain of the organization | +| `name` | `string` | The name of the organization | + +### `TicketingProject` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `name` | `string` | The name of the project | +| `description` | `string` | The description of the project | +| `organization_id` | `string` | The organization id related to the project | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `TicketingReadTicket` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `unified_custom_fields` | `object` | Custom Unified Fields configured in your StackOne project | +| `title` | `string` | The title or subject of the ticket | +| `creator_id` | `string` | The creator of the ticket | +| `reporters` | `array` | Users who reported the ticket | +| `assignees` | `array` | Agents assigned to the ticket | +| `content` | `array` | Array of content associated with the ticket | +| `parent_id` | `string` | ID of the parent ticket if this is a sub-ticket | +| `priority` | `TicketingTicketPriorityTypeEnum` | Priority of the ticket | +| `tags` | `array` | The tags of the ticket | +| `projects` | `array` | Projects the ticket belongs to | +| `ticket_number` | `string` | The unique ticket number or reference ID | +| `type` | `TicketingTicketType` | The type of the ticket | +| `closed_at` | `string` | The date the ticket was closed | +| `ticket_url` | `string` | URL to view the ticket in the source system | +| `status` | `TicketingTicketStatus` | Current status of the ticket | +| `organization` | `TicketingOrganization` | Organization associated with the ticket | +| `components` | `array` | Components associated with the ticket | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `TicketingTicketStatus` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The id of the ticket status. | +| `type` | `TicketingTicketStatusTypeEnum` | The type of this status | +| `name` | `string` | The name of the ticket status. | + +### `TicketingTicketType` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The id of the ticket type. | +| `name` | `string` | The name of the ticket type. | +| `project_id` | `string` | The project the ticket type belongs to. | + +### `TicketingUser` +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Unique identifier | +| `remote_id` | `string` | Provider's unique identifier | +| `type` | `TicketingUserTypeEnum` | | +| `name` | `string` | John Doe | +| `primary_email` | `string` | The user's primary email address | +| `primary_phone` | `string` | The user's primary phone number | +| `username` | `string` | The username of the user in the provider system | +| `active` | `object` | If the user is active | +| `first_name` | `string` | The first name of the user | +| `last_name` | `string` | The last name of the user | +| `customer_account_reference` | `string` | The unique account reference assigned as an external user (e.g. the customer account identifier r... | +| `created_at` | `string` | The timestamp when the record was created | +| `updated_at` | `string` | The timestamp when the record was last updated | + +### `UnifiedError` +| Field | Type | Description | +|-------|------|-------------| +| `statusCode` | `number` | HTTP status code | +| `message` | `string` | Error message | +| `headers` | `object` | Response headers | + +--- + +## Enums + +| Enum | Values | +|------|--------| +| `FileFormatEnum` | `ez`, `aw`, `atom`, `atomcat`, `atomdeleted`, `atomsvc`, `dwd`, `held`, `rsat`, `bdoc`, `xcs`, `ccxml`, `cdfx`, `cdmia`, `cdmic`, `cdmid`, `cdmio`, `cdmiq`, `cu`, `mpd`, `davmount`, `dbk`, `dssc`, `xdssc`, `es`, `ecma`, `emma`, `emotionml`, `epub`, `exi`, `exp`, `fdt`, `pfr`, `geojson`, `gml`, `gpx`, `gxf`, `gz`, `hjson`, `stk`, `ink`, `inkml`, `ipfix`, `its`, `jar`, `war`, `ear`, `ser`, `class`, `js`, `mjs`, `json`, `map`, `json5`, `jsonml`, `jsonld`, `lgr`, `lostxml`, `hqx`, `cpt`, `mads`, `webmanifest`, `mrc`, `mrcx`, `ma`, `nb`, `mb`, `mathml`, `mbox`, `mscml`, `metalink`, `meta4`, `mets`, `maei`, `musd`, `mods`, `m21`, `mp21`, `mp4s`, `m4p`, `doc`, `dot`, `mxf`, `nq`, `nt`, `cjs`, `bin`, `dms`, `lrf`, `mar`, `so`, `dist`, `distz`, `pkg`, `bpk`, `dump`, `elc`, `deploy`, `exe`, `dll`, `deb`, `dmg`, `iso`, `img`, `msi`, `msp`, `msm`, `buffer`, `oda`, `opf`, `ogx`, `omdoc`, `onetoc`, `onetoc2`, `onetmp`, `onepkg`, `oxps`, `relo`, `xer`, `pdf`, `pgp`, `asc`, `sig`, `prf`, `p10`, `p7m`, `p7c`, `p7s`, `p8`, `ac`, `cer`, `crl`, `pkipath`, `pki`, `pls`, `ai`, `eps`, `ps`, `provx`, `pskcxml`, `raml`, `rdf`, `owl`, `rif`, `rnc`, `rl`, `rld`, `rs`, `rapd`, `sls`, `rusd`, `gbr`, `mft`, `roa`, `rsd`, `rss`, `rtf`, `sbml`, `scq`, `scs`, `spq`, `spp`, `sdp`, `senmlx`, `sensmlx`, `setpay`, `setreg`, `shf`, `siv`, `sieve`, `smi`, `smil`, `rq`, `srx`, `gram`, `grxml`, `sru`, `ssdl`, `ssml`, `swidtag`, `tei`, `teicorpus`, `tfi`, `tsd`, `toml`, `trig`, `ttml`, `ubj`, `rsheet`, `td`, `vxml`, `wasm`, `wgt`, `hlp`, `wsdl`, `wspolicy`, `xaml`, `xav`, `xca`, `xdf`, `xel`, `xns`, `xenc`, `xhtml`, `xht`, `xlf`, `xml`, `xsl`, `xsd`, `rng`, `dtd`, `xop`, `xpl`, `*xsl`, `xslt`, `xspf`, `mxml`, `xhvml`, `xvml`, `xvm`, `yang`, `yin`, `zip`, `*3gpp`, `adp`, `amr`, `au`, `snd`, `mid`, `midi`, `kar`, `rmi`, `mxmf`, `*mp3`, `m4a`, `mp4a`, `mpga`, `mp2`, `mp2a`, `mp3`, `m2a`, `m3a`, `oga`, `ogg`, `spx`, `opus`, `s3m`, `sil`, `wav`, `*wav`, `weba`, `xm`, `ttc`, `otf`, `ttf`, `woff`, `woff2`, `exr`, `apng`, `avif`, `bmp`, `cgm`, `drle`, `emf`, `fits`, `g3`, `gif`, `heic`, `heics`, `heif`, `heifs`, `hej2`, `hsj2`, `ief`, `jls`, `jp2`, `jpg2`, `jpeg`, `jpg`, `jpe`, `jph`, `jhc`, `jpm`, `jpx`, `jpf`, `jxr`, `jxra`, `jxrs`, `jxs`, `jxsc`, `jxsi`, `jxss`, `ktx`, `ktx2`, `png`, `sgi`, `svg`, `svgz`, `t38`, `tif`, `tiff`, `tfx`, `webp`, `wmf`, `disposition-notification`, `u8msg`, `u8dsn`, `u8mdn`, `u8hdr`, `eml`, `mime`, `3mf`, `gltf`, `glb`, `igs`, `iges`, `msh`, `mesh`, `silo`, `mtl`, `obj`, `stpx`, `stpz`, `stpxz`, `stl`, `wrl`, `vrml`, `*x3db`, `x3dbz`, `x3db`, `*x3dv`, `x3dvz`, `x3d`, `x3dz`, `x3dv`, `appcache`, `manifest`, `ics`, `ifb`, `coffee`, `litcoffee`, `css`, `csv`, `html`, `htm`, `shtml`, `jade`, `jsx`, `less`, `markdown`, `md`, `mml`, `mdx`, `n3`, `txt`, `text`, `conf`, `def`, `list`, `log`, `in`, `ini`, `rtx`, `*rtf`, `sgml`, `sgm`, `shex`, `slim`, `slm`, `spdx`, `stylus`, `styl`, `tsv`, `t`, `tr`, `roff`, `man`, `me`, `ms`, `ttl`, `uri`, `uris`, `urls`, `vcard`, `vtt`, `*xml`, `yaml`, `yml`, `3gp`, `3gpp`, `3g2`, `h261`, `h263`, `h264`, `m4s`, `jpgv`, `*jpm`, `jpgm`, `mj2`, `mjp2`, `ts`, `mp4`, `mp4v`, `mpg4`, `mpeg`, `mpg`, `mpe`, `m1v`, `m2v`, `ogv`, `qt`, `mov`, `webm`, `cww`, `1km`, `plb`, `psb`, `pvb`, `tcap`, `pwn`, `aso`, `imp`, `acu`, `atc`, `acutc`, `air`, `fcdt`, `fxp`, `fxpl`, `xdp`, `xfdf`, `ahead`, `azf`, `azs`, `azw`, `acc`, `ami`, `apk`, `cii`, `fti`, `atx`, `mpkg`, `key`, `m3u8`, `numbers`, `pages`, `pkpass`, `swi`, `iota`, `aep`, `bmml`, `mpm`, `bmi`, `rep`, `cdxml`, `mmd`, `cdy`, `csl`, `cla`, `rp9`, `c4g`, `c4d`, `c4f`, `c4p`, `c4u`, `c11amc`, `c11amz`, `csp`, `cdbcmsg`, `cmc`, `clkx`, `clkk`, `clkp`, `clkt`, `clkw`, `wbs`, `pml`, `ppd`, `car`, `pcurl`, `dart`, `rdz`, `dbf`, `uvf`, `uvvf`, `uvd`, `uvvd`, `uvt`, `uvvt`, `uvx`, `uvvx`, `uvz`, `uvvz`, `fe_launch`, `dna`, `mlp`, `mle`, `dpg`, `dfac`, `kpxx`, `ait`, `svc`, `geo`, `mag`, `nml`, `esf`, `msf`, `qam`, `slt`, `ssf`, `es3`, `et3`, `ez2`, `ez3`, `fdf`, `mseed`, `seed`, `dataless`, `gph`, `ftc`, `fm`, `frame`, `maker`, `book`, `fnc`, `ltf`, `fsc`, `oas`, `oa2`, `oa3`, `fg5`, `bh2`, `ddd`, `xdw`, `xbd`, `fzs`, `txd`, `ggb`, `ggt`, `gex`, `gre`, `gxt`, `g2w`, `g3w`, `gmx`, `gdoc`, `gslides`, `gsheet`, `kml`, `kmz`, `gqf`, `gqs`, `gac`, `ghf`, `gim`, `grv`, `gtm`, `tpl`, `vcg`, `hal`, `zmm`, `hbci`, `les`, `hpgl`, `hpid`, `hps`, `jlt`, `pcl`, `pclxl`, `sfd-hdstx`, `mpy`, `afp`, `listafp`, `list3820`, `irm`, `sc`, `icc`, `icm`, `igl`, `ivp`, `ivu`, `igm`, `xpw`, `xpx`, `i2g`, `qbo`, `qfx`, `rcprofile`, `irp`, `xpr`, `fcs`, `jam`, `rms`, `jisp`, `joda`, `ktz`, `ktr`, `karbon`, `chrt`, `kfo`, `flw`, `kon`, `kpr`, `kpt`, `ksp`, `kwd`, `kwt`, `htke`, `kia`, `kne`, `knp`, `skp`, `skd`, `skt`, `skm`, `sse`, `lasxml`, `lbd`, `lbe`, `apr`, `pre`, `nsf`, `org`, `scm`, `lwp`, `portpkg`, `mvt`, `mcd`, `mc1`, `cdkey`, `mwf`, `mfm`, `flo`, `igx`, `mif`, `daf`, `dis`, `mbk`, `mqy`, `msl`, `plc`, `txf`, `mpn`, `mpc`, `xul`, `cil`, `cab`, `xls`, `xlm`, `xla`, `xlc`, `xlt`, `xlw`, `xlam`, `xlsb`, `xlsm`, `xltm`, `eot`, `chm`, `ims`, `lrm`, `thmx`, `msg`, `cat`, `*stl`, `ppt`, `pps`, `pot`, `ppam`, `pptm`, `sldm`, `ppsm`, `potm`, `mpp`, `mpt`, `docm`, `dotm`, `wps`, `wks`, `wcm`, `wdb`, `wpl`, `xps`, `mseq`, `mus`, `msty`, `taglet`, `nlu`, `ntf`, `nitf`, `nnd`, `nns`, `nnw`, `*ac`, `ngdat`, `n-gage`, `rpst`, `rpss`, `edm`, `edx`, `ext`, `odc`, `otc`, `odb`, `odf`, `odft`, `odg`, `otg`, `odi`, `oti`, `odp`, `otp`, `ods`, `ots`, `odt`, `odm`, `ott`, `oth`, `xo`, `dd2`, `obgx`, `oxt`, `osm`, `pptx`, `sldx`, `ppsx`, `potx`, `xlsx`, `xltx`, `docx`, `dotx`, `mgp`, `dp`, `esa`, `pdb`, `pqa`, `oprc`, `paw`, `str`, `ei6`, `efif`, `wg`, `plf`, `pbd`, `box`, `mgz`, `qps`, `ptid`, `qxd`, `qxt`, `qwd`, `qwt`, `qxl`, `qxb`, `rar`, `bed`, `mxl`, `musicxml`, `cryptonote`, `cod`, `rm`, `rmvb`, `link66`, `st`, `see`, `sema`, `semd`, `semf`, `ifm`, `itp`, `iif`, `ipk`, `twd`, `twds`, `mmf`, `teacher`, `fo`, `sdkm`, `sdkd`, `dxp`, `sfs`, `sdc`, `sda`, `sdd`, `smf`, `sdw`, `vor`, `sgl`, `smzip`, `sm`, `wadl`, `sxc`, `stc`, `sxd`, `std`, `sxi`, `sti`, `sxm`, `sxw`, `sxg`, `stw`, `sus`, `susp`, `svd`, `sis`, `sisx`, `xsm`, `bdm`, `xdm`, `ddf`, `tao`, `pcap`, `cap`, `dmp`, `tmo`, `tpt`, `mxs`, `tra`, `ufd`, `ufdl`, `utz`, `umj`, `unityweb`, `uoml`, `vcx`, `vsd`, `vst`, `vss`, `vsw`, `vis`, `vsf`, `wbxml`, `wmlc`, `wmlsc`, `wtb`, `nbp`, `wpd`, `wqd`, `stf`, `xar`, `xfdl`, `hvd`, `hvs`, `hvp`, `osf`, `osfpvg`, `saf`, `spf`, `cmp`, `zir`, `zirz`, `zaz`, `7z`, `abw`, `ace`, `*dmg`, `arj`, `aab`, `x32`, `u32`, `vox`, `aam`, `aas`, `bcpio`, `*bdoc`, `torrent`, `blb`, `blorb`, `bz`, `bz2`, `boz`, `cbr`, `cba`, `cbt`, `cbz`, `cb7`, `vcd`, `cfs`, `chat`, `pgn`, `crx`, `cco`, `nsc`, `cpio`, `csh`, `*deb`, `udeb`, `dgc`, `dir`, `dcr`, `dxr`, `cst`, `cct`, `cxt`, `w3d`, `fgd`, `swa`, `wad`, `ncx`, `dtb`, `res`, `dvi`, `evy`, `eva`, `bdf`, `gsf`, `psf`, `pcf`, `snf`, `pfa`, `pfb`, `pfm`, `afm`, `arc`, `spl`, `gca`, `ulx`, `gnumeric`, `gramps`, `gtar`, `hdf`, `php`, `install`, `*iso`, `*key`, `*numbers`, `*pages`, `jardiff`, `jnlp`, `kdbx`, `latex`, `luac`, `lzh`, `lha`, `run`, `mie`, `prc`, `mobi`, `application`, `lnk`, `wmd`, `wmz`, `xbap`, `mdb`, `obd`, `crd`, `clp`, `*exe`, `*dll`, `com`, `bat`, `*msi`, `mvb`, `m13`, `m14`, `*wmf`, `*wmz`, `*emf`, `emz`, `mny`, `pub`, `scd`, `trm`, `wri`, `nc`, `cdf`, `pac`, `nzb`, `pl`, `pm`, `*prc`, `*pdb`, `p12`, `pfx`, `p7b`, `spc`, `p7r`, `*rar`, `rpm`, `ris`, `sea`, `sh`, `shar`, `swf`, `xap`, `sql`, `sit`, `sitx`, `srt`, `sv4cpio`, `sv4crc`, `t3`, `gam`, `tar`, `tcl`, `tk`, `tex`, `tfm`, `texinfo`, `texi`, `*obj`, `ustar`, `hdd`, `ova`, `ovf`, `vbox`, `vbox-extpack`, `vdi`, `vhd`, `vmdk`, `src`, `webapp`, `der`, `crt`, `pem`, `fig`, `*xlf`, `xpi`, `xz`, `z1`, `z2`, `z3`, `z4`, `z5`, `z6`, `z7`, `z8`, `uva`, `uvva`, `eol`, `dra`, `dts`, `dtshd`, `lvp`, `pya`, `ecelp4800`, `ecelp7470`, `ecelp9600`, `rip`, `aac`, `aif`, `aiff`, `aifc`, `caf`, `flac`, `*m4a`, `mka`, `m3u`, `wax`, `wma`, `ram`, `ra`, `rmp`, `*ra`, `cdx`, `cif`, `cmdf`, `cml`, `csml`, `xyz`, `btif`, `pti`, `psd`, `azv`, `uvi`, `uvvi`, `uvg`, `uvvg`, `djvu`, `djv`, `*sub`, `dwg`, `dxf`, `fbs`, `fpx`, `fst`, `mmr`, `rlc`, `ico`, `dds`, `mdi`, `wdp`, `npx`, `b16`, `tap`, `vtf`, `wbmp`, `xif`, `pcx`, `3ds`, `ras`, `cmx`, `fh`, `fhc`, `fh4`, `fh5`, `fh7`, `*ico`, `jng`, `sid`, `*bmp`, `*pcx`, `pic`, `pct`, `pnm`, `pbm`, `pgm`, `ppm`, `rgb`, `tga`, `xbm`, `xpm`, `xwd`, `wsc`, `dae`, `dwf`, `gdl`, `gtw`, `mts`, `ogex`, `x_b`, `x_t`, `vds`, `usdz`, `bsp`, `vtu`, `dsc`, `curl`, `dcurl`, `mcurl`, `scurl`, `sub`, `fly`, `flx`, `gv`, `3dml`, `spot`, `jad`, `wml`, `wmls`, `s`, `asm`, `c`, `cc`, `cxx`, `cpp`, `h`, `hh`, `dic`, `htc`, `f`, `for`, `f77`, `f90`, `hbs`, `java`, `lua`, `mkd`, `nfo`, `opml`, `*org`, `p`, `pas`, `pde`, `sass`, `scss`, `etx`, `sfv`, `ymp`, `uu`, `vcs`, `vcf`, `uvh`, `uvvh`, `uvm`, `uvvm`, `uvp`, `uvvp`, `uvs`, `uvvs`, `uvv`, `uvvv`, `dvb`, `fvt`, `mxu`, `m4u`, `pyv`, `uvu`, `uvvu`, `viv`, `f4v`, `fli`, `flv`, `m4v`, `mkv`, `mk3d`, `mks`, `mng`, `asf`, `asx`, `vob`, `wm`, `wmv`, `wmx`, `wvx`, `avi`, `movie`, `smv`, `ice`, `mht` | +| `TicketingTicketPriorityTypeEnum` | `lowest`, `low`, `medium`, `high`, `highest` | +| `TicketingTicketStatusTypeEnum` | `to-do`, `in-progress`, `closed` | +| `TicketingUserTypeEnum` | `agent`, `contact`, `bot` | diff --git a/.claude/plugins/stackone-connector-builder/references/unified-schemas.md b/.claude/plugins/stackone-connector-builder/references/unified-schemas.md new file mode 100644 index 0000000..4e708be --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/unified-schemas.md @@ -0,0 +1,31 @@ +# StackOne Unified Schemas — Category Index + +Quick index of all StackOne unification categories. When a builder chooses a category, load the corresponding file from `references/schemas/` — do not load all files at once. + +--- + +## Categories + +| Category | File | Key Resources | Use Cases | +|----------|------|--------------|-----------| +| `hris` | `schemas/hris.md` | employees, employments, departments, locations, groups, benefits, cost_centers, jobs, shifts | HR data sync, workforce management, payroll | +| `ats` | `schemas/ats.md` | jobs, candidates, applications, interviews, offers, departments | Recruiting workflows, applicant tracking | +| `crm` | `schemas/crm.md` | contacts, accounts, lists, activities | Sales pipelines, customer data | +| `lms` | `schemas/lms.md` | courses, content, users, completions, enrollments | Learning management, training tracking | +| `iam` | `schemas/iam.md` | users, groups, roles, permissions | Identity and access management | +| `ticketing` | `schemas/ticketing.md` | tickets, projects, collections, users | Support desk, project tracking | +| `documents` | `schemas/documents.md` | files, folders, drives | Document storage, file management | +| `marketing` | `schemas/marketing.md` | campaigns, email_templates, contacts, push, sms | Marketing automation, campaign management | +| `accounting` | `schemas/accounting.md` | accounts, transactions, contacts, ledger_accounts | Financial data, accounting integrations | + +--- + +## How to use this index + +1. Show the builder the table above to help them identify the right category. +2. Once they choose (e.g., `hris`), read **only** `${CLAUDE_PLUGIN_ROOT}/references/schemas/hris.md`. +3. Use the loaded file's **Endpoints** section to show available API operations. +4. Use the **Models** section to show schema fields for each resource. +5. Use the **Enums** section when helping with field mapping for enum-type fields. + +Do not load multiple schema files at once — each file is comprehensive and loading only the chosen category keeps context focused. diff --git a/.claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md new file mode 100644 index 0000000..7e9f0f3 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md @@ -0,0 +1,140 @@ +--- +name: check-connector +description: Step 2 of building a unified connector. Checks if the StackOne CLI is available, looks up whether the connector already exists in the StackOne index, and either pulls it down or scaffolds a new one based on auth type. +invoke: check-connector +--- + +# Check Connector + +Step 2 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. If `provider` is missing, tell the builder: +> "No provider found in session. Run `/choose-schema` first to set up your provider and schema." + +Greet with context: +> "Checking connector for provider: `{{provider}}`" + +--- + +## Step 1: Detect CLI availability + +Run: +```bash +npx @stackone/cli --version +``` + +**If successful:** Save `"cli_available": true` to session. Continue to Step 2. + +**If it errors or is not found:** Save `"cli_available": false` to session. + +Tell the builder: +> "The StackOne CLI isn't available in this environment. That's fine — I'll guide you through manual scaffolding instead." + +Skip to **Path B: Manual scaffold** below. + +--- + +## Step 2: Check if connector exists in StackOne index + +Run: +```bash +npx @stackone/cli list +``` + +Search the output for the provider name (case-insensitive match). + +**If found:** Tell the builder: +> "`{{provider}}` exists in the StackOne connector index. I'll pull the base config down for you." + +Proceed to **Path A: Pull existing connector**. + +**If not found:** Tell the builder: +> "`{{provider}}` isn't in the StackOne connector index yet — you'll be building this from scratch." + +Proceed to **Path B: Scaffold new connector**. + +--- + +## Path A: Pull existing connector + +Run: +```bash +npx @stackone/cli pull {{provider}} +``` + +Verify `src/configs/{{provider}}/` was created and contains at least one `.yaml` file. + +List the files found and confirm: +> "Pulled connector config: +> - `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` +> - ..." +> +> "This is a starting point — we'll add unified actions on top. Does the folder look right?" + +Save to session: +```json +{ + "connector_exists": true, + "connector_path": "src/configs/{{provider}}" +} +``` + +--- + +## Path B: Scaffold new connector + +Ask the builder about authentication: +> "What authentication type does `{{provider}}` use? +> +> **A) API Key / Bearer token** — a static key or token passed in a header +> **B) OAuth 2.0** — user authorizes via browser, you get access tokens +> **C) Basic Auth** — username and password +> **D) Something else** — I'll help you figure it out" + +Read `${CLAUDE_PLUGIN_ROOT}/references/connector-patterns.md` and use the matching pattern to create `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. + +**If CLI is available, try scaffolding first:** +```bash +npx @stackone/cli scaffold {{provider}} +``` + +Then open the generated file and update the `authentication` block to match the chosen auth type using the patterns from `connector-patterns.md`. + +**If CLI is not available:** Write the starter YAML directly using the matching pattern from `connector-patterns.md`. + +After creating the file, tell the builder: +> "Created starter connector at `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. +> +> Before testing, add your credentials to `.env`: +> ``` +> {{PROVIDER_CREDENTIAL_NAME}}=your_value_here +> ``` +> +> The variable name is shown in the `authentication` block of your connector YAML." + +Save to session: +```json +{ + "connector_exists": false, + "connector_path": "src/configs/{{provider}}", + "auth_type": "{{auth_type}}" +} +``` + +--- + +## Handoff + +After the connector is set up: +> "Connector is ready at `src/configs/{{provider}}/`. ✓ +> +> **CLI available:** {{cli_available}} +> **Connector was pre-existing:** {{connector_exists}} +> **Auth type:** {{auth_type}} +> +> Next step: decide which resources and operations to build. +> Run `/scope-actions` to continue." + +Update `session_step` to `"scope-actions"` in the session file. diff --git a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md new file mode 100644 index 0000000..3310807 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md @@ -0,0 +1,173 @@ +--- +name: choose-schema +description: First step of building a unified connector. Guides the builder to choose or define the output schema — StackOne built-in, custom file, or define inline. Saves schema choice to session file. +invoke: choose-schema +--- + +# Choose Schema + +Step 1 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json` from the project root if it exists. + +If it exists and `schema` is already set, greet the builder: +> "Resuming session — schema already set to `{{schema}}` ({{schema_source}}). Run `/check-connector` to continue, or type `reset` to start over." + +If it does not exist or `schema` is empty, proceed below. + +--- + +## Step 1: Ask for the provider name + +Ask: +> "What is the name of the provider you want to build a connector for? (e.g., `bamboohr`, `workday`, `salesforce`)" + +Save as `provider` in session (lowercase, hyphenated). + +--- + +## Step 2: Choose schema source + +Ask: +> "What output schema will your connector map data to? Choose one: +> +> **A) StackOne built-in schema** — HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, Marketing +> **B) Point to an existing schema file** — You have a JSON/YAML schema in your project +> **C) Define a new custom schema** — I'll help you define fields interactively +> +> Which fits your use case? (A/B/C)" + +--- + +## Path A: StackOne built-in schema + +Read `${CLAUDE_PLUGIN_ROOT}/references/unified-schemas.md` for the category index. + +Present the category table and ask: +> "Which StackOne category fits your use case? (hris / ats / crm / marketing / lms / iam / ticketing / documents / accounting)" + +**Once the category is chosen, load only that category's schema file:** + +| Category chosen | File to read | +|----------------|-------------| +| `hris` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/hris.md` | +| `ats` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/ats.md` | +| `crm` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/crm.md` | +| `lms` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/lms.md` | +| `iam` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/iam.md` | +| `ticketing` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/ticketing.md` | +| `documents` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/documents.md` | +| `marketing` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/marketing.md` | +| `accounting` | `${CLAUDE_PLUGIN_ROOT}/references/schemas/accounting.md` | + +Read the schema file. Use the **Endpoints** section to show the builder what API operations are available, and the **Models** section to show the fields for each resource. + +Ask: +> "Which resource(s) do you need to expose? Here's what's available in `{{category}}`:" + +List the model names from the schema file (e.g., `Employee`, `Employment`, `HRISDepartment`). + +Once resources are chosen, show the fields table for each from the loaded schema file. + +Ask: +> "Do these fields cover your needs, or are there fields in your use case that aren't listed here?" + +If fields are missing, note them — they may need custom field mapping or a different resource. + +Save to session: +```json +{ + "schema_source": "builtin", + "schema": "", + "schema_file": "references/schemas/.md", + "resources": ["", ""], + "schema_fields": [] +} +``` + +--- + +## Path B: Existing schema file + +Ask: +> "What is the path to your schema file? (e.g., `schemas/employee.json`)" + +Read the file. Identify the fields: name, type, whether required. + +Present a summary: +> "Found {{N}} fields in your schema: +> - `id` (string, required) +> - `first_name` (string, required) +> - ..." + +Ask: +> "Does this look correct? Any fields I should know are enums or nested objects?" + +Save to session: +```json +{ + "schema_source": "file", + "schema": "custom", + "schema_file": "", + "resources": ["custom"], + "schema_fields": [{ "name": "...", "type": "...", "required": true }] +} +``` + +--- + +## Path C: Define custom schema interactively + +Explain: +> "Let's define your schema field by field. For each field I'll ask for: +> - **Name** — the field key in your output (e.g., `first_name`) +> - **Type** — string, number, boolean, date, datetime, enum, array, object +> - **Required** — yes or no +> +> For enum fields I'll also ask for the allowed values. Type `done` when finished." + +Collect fields one at a time. For `enum` types, ask for the allowed values. For `array` types, ask for the item type. + +Show a running summary after each field is added. + +When done, present the full schema: +> "Here's your schema: +> +> | Field | Type | Required | +> |-------|------|---------| +> | `id` | string | yes | +> | `status` | enum (active, inactive) | yes | +> | ... | +> +> Does this look right?" + +Save to session: +```json +{ + "schema_source": "custom", + "schema": "custom", + "resources": ["custom"], + "schema_fields": [ + { "name": "id", "type": "string", "required": true }, + { "name": "status", "type": "enum", "required": true, "enum_values": ["active", "inactive"] } + ] +} +``` + +--- + +## Handoff + +After saving the session file, confirm: +> "Schema saved. ✓ +> +> **Provider:** `{{provider}}` +> **Schema:** `{{schema}}` ({{schema_source}}) +> **Resources:** {{resources}} +> +> Next step: check if your connector already exists and set up the project. +> Run `/check-connector` to continue." + +Update `session_step` to `"check-connector"` in the session file. diff --git a/.claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md new file mode 100644 index 0000000..d7cc1b4 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md @@ -0,0 +1,118 @@ +--- +name: map-fields +description: Step 4 of building a unified connector. Guides the builder through mapping provider API fields to their schema using fieldConfigs. Writes or updates the connector YAML partial files for each resource. +invoke: map-fields +--- + +# Map Fields + +Step 4 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm context: +> "Mapping fields for `{{provider}}` → `{{schema}}` | Resources: {{resources}} | Actions: {{action_scope}}" + +If `action_scope` is missing: +> "No action scope found. Run `/scope-actions` first." + +Read `${CLAUDE_PLUGIN_ROOT}/references/field-mapping-guide.md` and `${CLAUDE_PLUGIN_ROOT}/references/connector-patterns.md` before beginning. + +If `schema_source` is `"builtin"` and `schema_file` is set in session, also read `${CLAUDE_PLUGIN_ROOT}/{{schema_file}}` to get the exact field names, types, and enum values for the chosen category. Use this as the authoritative field reference when constructing `fieldConfigs` — the `targetFieldKey` must match the field names defined in the schema file exactly. + +--- + +## Step 1: Get the raw provider response + +Getting a real sample response makes field mapping accurate. + +**If `cli_available` is true in session:** +```bash +npx @stackone/cli test {{provider}} list_{{resource}} --debug +``` +This shows the raw JSON before any mapping. Use the output to identify exact field paths. + +**If CLI is not available:** Ask: +> "Could you paste an example response from `{{provider}}`'s `{{resource}}` API endpoint? Even a partial response with one or two records is enough. This helps me map the fields accurately." + +--- + +## Step 2: Build fieldConfigs for each resource + +Work through resources one at a time. + +For each schema field (from `schema_fields` in session or the built-in schema from `unified-schemas.md`): + +1. Show the field: e.g., `first_name (string, required)` +2. Ask: "What is the field path in the `{{provider}}` response? (e.g., `$.firstName`, `$.contact.first_name`)" +3. For enum fields: "What values does `{{provider}}` use? I'll map them to the schema values." +4. If the builder is unsure, reference the raw response sample together. + +Build the `fieldConfigs` array live as you collect mappings: +```yaml +fieldConfigs: + - targetFieldKey: id + expression: "$.id" + - targetFieldKey: first_name + expression: "$.firstName" + - targetFieldKey: employment_status + expression: "$.status" + enumMapper: + active: active + terminated: inactive +``` + +When all fields for a resource are mapped, show the complete list and ask: +> "Here's the mapping for `{{resource}}`. Anything missing or incorrect?" + +--- + +## Step 3: Write the partial YAML file + +For each resource, create or update: +`src/configs/{{provider}}/{{provider}}.{{resource}}.s1.partial.yaml` + +Use the unified action pattern from `connector-patterns.md`. For each action in scope: + +- `name: unified_{{action}}_{{resource}}` +- `actionType: {{action}}` +- `schemaType: unified` +- `schema: {{schema}}/{{resource}}` +- `entrypointUrl` and `entrypointHttpMethod` +- `fieldConfigs` from Step 2 +- `cursor` block for `list` actions +- All three steps: `request`, `map_fields` (version 2), `typecast` (version 2) +- For `get` actions: include the ID in `entrypointUrl` as `${inputs.id}` and add `id` to `inputs` + +Also update the main connector YAML to `$ref` each new partial if not already referenced. + +--- + +## Step 4: Review with the builder + +After writing each partial, show the file content and ask: +> "Here's the field mapping for `{{resource}}`. A few things to verify: +> 1. Do the `targetFieldKey` names match your schema exactly? +> 2. Are there any fields I missed? +> 3. For enum fields — did I capture all the provider's possible values? +> +> [show YAML]" + +Apply any corrections the builder requests before moving to the next resource. + +--- + +## Handoff + +After all resources are mapped and partials are written: +> "Field mapping complete. ✓ +> +> Files written: +> - `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` (updated with $refs) +> - `src/configs/{{provider}}/{{provider}}.employees.s1.partial.yaml` +> - ... +> +> Next step: validate the YAML configuration. +> Run `/validate-connector` to continue." + +Update `session_step` to `"validate-connector"` in the session file. diff --git a/.claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md new file mode 100644 index 0000000..53de354 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md @@ -0,0 +1,114 @@ +--- +name: scope-actions +description: Step 3 of building a unified connector. Asks the builder which resources and CRUD operations matter for their use case, then saves the confirmed action scope to the session file. +invoke: scope-actions +--- + +# Scope Actions + +Step 3 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. Greet with context: +> "Scoping actions for `{{provider}}` → `{{schema}}` connector." + +If `provider` or `schema` is missing: +> "Session is incomplete. Run `/choose-schema` first." + +--- + +## Step 1: Orient the builder on action types + +Give a brief overview before asking anything: +> "Unified connectors expose **actions** — operations customers can call via the StackOne API. For each resource you can expose: +> +> | Action | What it does | Example | +> |--------|-------------|---------| +> | `list` | Returns a paginated list of records | List all employees | +> | `get` | Returns a single record by ID | Get employee by ID | +> | `create` | Creates a new record | Create an employee | +> | `update` | Updates an existing record | Update employee fields | +> | `delete` | Removes a record | Delete an employee | +> +> Not all providers support all operations — we'll confirm what makes sense for your use case." + +--- + +## Step 2: Ask about the primary use case + +Ask: +> "What is the primary use case for this connector? This helps me recommend which actions to prioritize. +> +> Examples: +> - "Sync employee data into our internal system" → `list` + `get` +> - "Let customers create and update records from our app" → `create` + `update` +> - "Full two-way sync" → all five actions +> - "Just read data for reporting" → `list` + `get` only +> +> Describe your use case in a sentence or two." + +Save the use case description to session as `use_case`. + +--- + +## Step 3: Present a recommended scope + +Based on the session `resources` and the use case, recommend an action set. + +Example recommendation: +> "Based on your schema and use case, here's what I recommend: +> +> | Resource | Recommended Actions | +> |----------|-------------------| +> | `employees` | `list`, `get` | +> | `time_off` | `list`, `get`, `create` | +> +> Does this match what you need? You can add or remove any actions — just let me know." + +Allow the builder to adjust freely. Confirm the final scope before saving. + +--- + +## Step 4: Flag potential provider gaps + +Ask: +> "Do you know if `{{provider}}`'s API supports all these operations? +> - Does it have list/get endpoints for each resource? +> - Does it support creating or updating via API, or is it read-only? +> +> If you're unsure, that's fine — we'll discover this during testing." + +Note any known limitations in the session. + +--- + +## Step 5: Save to session + +```json +{ + "action_scope": { + "employees": ["list", "get"], + "time_off": ["list", "get", "create"] + }, + "use_case": "Sync HR data into customer's internal system", + "known_limitations": ["time_off.delete not supported by provider API"] +} +``` + +--- + +## Handoff + +Confirm and hand off: +> "Action scope confirmed. ✓ +> +> | Resource | Actions | +> |----------|---------| +> | `employees` | list, get | +> | `time_off` | list, get, create | +> +> Next step: map provider fields to your schema. +> Run `/map-fields` to continue." + +Update `session_step` to `"map-fields"` in the session file. diff --git a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md new file mode 100644 index 0000000..21e3de8 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md @@ -0,0 +1,145 @@ +--- +name: stackone-connector-builder +description: Interactive wizard for building a unified Falcon connector from scratch. Guides external builders through schema selection, connector setup, action scoping, field mapping, validation, and testing. Auto-triggers when someone asks to build a new connector, integrate with a new provider, or create a unified connector. +invoke: build-connector +--- + +# Build Connector + +End-to-end wizard for building a unified StackOne Falcon connector. + +## Quick Reference + +Run steps in order, or invoke any sub-skill directly to jump to that phase: + +| Step | Command | What it does | +|------|---------|-------------| +| 1 | `/choose-schema` | Pick or define your output schema | +| 2 | `/check-connector` | Check if connector exists, pull or scaffold | +| 3 | `/scope-actions` | Decide which resources and operations to expose | +| 4 | `/map-fields` | Map provider API fields to your schema | +| 5 | `/validate-connector` | Validate the YAML configuration | +| 6 | `/test-connector` | Test live against the provider API | + +Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. + +--- + +## Starting the Wizard + +Check for an existing `.connector-build-session.json`. If found: +> "Found an existing session: +> - **Provider:** `{{provider}}` +> - **Schema:** `{{schema}}` +> - **Last step completed:** `{{session_step}}` +> +> Would you like to: +> - **Resume** from `{{session_step}}` +> - **Restart** from the beginning (clears session)" + +If no session exists, greet the builder and proceed to Step 1: +> "Welcome to the StackOne connector builder. I'll guide you through building a unified Falcon connector step by step. +> +> You can run `/build-connector` to go through the full flow, or jump to any individual step with `/choose-schema`, `/check-connector`, `/scope-actions`, `/map-fields`, `/validate-connector`, or `/test-connector`." + +--- + +## Step 1 — Choose Schema + +Execute the full `/choose-schema` skill logic. + +**Outcome saved to session:** +- `provider` — the provider name (e.g., `bamboohr`) +- `schema` — the target category or `custom` +- `schema_source` — `builtin`, `file`, or `custom` +- `resources` — list of resources to build +- `schema_fields` — field definitions (for custom/file schemas) + +--- + +## Step 2 — Check Connector + +Execute the full `/check-connector` skill logic. + +**Outcome saved to session:** +- `cli_available` — whether the StackOne CLI is usable +- `connector_exists` — whether a base config was pulled from the index +- `connector_path` — where the config lives +- `auth_type` — the authentication method + +--- + +## Step 3 — Scope Actions + +Execute the full `/scope-actions` skill logic. + +**Outcome saved to session:** +- `action_scope` — `{ resource: [actions] }` map +- `use_case` — builder's description of their goal +- `known_limitations` — any flagged gaps + +--- + +## Step 4 — Map Fields + +Execute the full `/map-fields` skill logic. + +**Outcome:** Partial YAML files written to `src/configs/{{provider}}/` + +--- + +## Step 5 — Validate + +Execute the full `/validate-connector` skill logic. + +**Outcome saved to session:** +- `validated: true` + +--- + +## Step 6 — Test + +Execute the full `/test-connector` skill logic. + +**Outcome saved to session:** +- `tested: true` +- `completed_at` + +--- + +## Session File Schema + +`.connector-build-session.json` lives at the project root and carries all state between steps: + +```json +{ + "provider": "bamboohr", + "schema": "hris", + "schema_source": "builtin", + "schema_fields": [], + "resources": ["employees", "time_off"], + "cli_available": true, + "connector_exists": false, + "connector_path": "src/configs/bamboohr", + "auth_type": "api_key", + "action_scope": { + "employees": ["list", "get"], + "time_off": ["list", "get", "create"] + }, + "use_case": "Sync HR data into customer's internal system", + "known_limitations": [], + "validated": false, + "tested": false, + "session_step": "map-fields", + "completed_at": null +} +``` + +--- + +## Rules + +- Do not write YAML files until Step 4 (`map-fields`) +- Do not run live tests until Step 5 (`validate-connector`) has passed +- Do not delete the session file unless the builder explicitly requests a reset +- Always read session context at the start of each step — never ask the builder for information already in the session diff --git a/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md new file mode 100644 index 0000000..9f38150 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md @@ -0,0 +1,130 @@ +--- +name: test-connector +description: Step 6 (final) of building a unified connector. Runs live action tests against the provider API, verifies field mapping output matches the schema, and surfaces failures with actionable fix guidance. +invoke: test-connector +--- + +# Test Connector + +Step 6 — the final step of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. + +If `validated` is not `true`, warn: +> "The connector hasn't been validated yet. Run `/validate-connector` first — it catches structural issues before you spend time on live tests." + +If `cli_available` is `false`: +> "The StackOne CLI isn't available, so I can't run automated tests. I'll guide you through manual verification instead." + +--- + +## Step 1: Credentials check + +Ask before running anything: +> "Do you have credentials for `{{provider}}` ready in your `.env` file? +> +> You'll need a line like: +> ``` +> {{PROVIDER_CREDENTIAL_NAME}}=your_value_here +> ``` +> +> The exact variable name is in `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` — look in the `authentication` block for the `args` key names." + +Wait for confirmation before proceeding. + +--- + +## Step 2: Test each action + +Work through each resource and action in `action_scope`. Test in this order: `list` → `get` → `create` → `update` → `delete`. + +**If CLI is available:** +```bash +npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} +``` + +**If CLI is not available:** +Ask the builder to call the provider API manually and paste the response. Manually verify the `fieldConfigs` expressions against the pasted response. + +--- + +## Step 3: Verify output for each action + +**For `list` actions:** +- Does the response contain a `data` array? +- Does each item in `data` have all required schema fields populated (non-null)? +- If the provider has multiple pages — is there a `next_cursor` or `next_page` token in the response? +- Are there any unexpected null fields that should have values? + +**For `get` actions:** +- Single object returned with the correct schema shape? +- Does the `id` field match what was requested? + +**For `create` actions:** +- Record created successfully? +- Does the response contain the new record's `id`? + +**For `update` actions:** +- Changes reflected in the response? + +**For `delete` actions:** +- Returns success (200 or 204)? + +--- + +## Step 4: Diagnose and fix failures + +**Field is null or missing in output:** +> "Field `{{field}}` is missing. Let's debug: +> 1. Run with `--debug` to see the raw provider response: `npx @stackone/cli test {{provider}} {{action}} --debug` +> 2. Find `{{field}}` in the raw response — what is the actual JSONPath? +> 3. Update the `expression` in `fieldConfigs` to match." + +Show the fix in the partial YAML and re-test. + +**Enum value passes through unmapped:** +> "The value `{{value}}` for `{{field}}` isn't in the `enumMapper`. Add it:" +```yaml +enumMapper: + {{value}}: {{schema_value}} +``` + +**Pagination not advancing:** +> "The next page isn't being fetched. Check: +> 1. What pagination style does `{{provider}}` use? (page number, cursor token, or link header) +> 2. In the raw response, where does the 'next page' value live? +> 3. Update the `cursor.responsePath` in the action to point to that field." + +**Authentication error (401/403):** +> "Authentication failed. Check: +> 1. Is the `.env` variable name correct? +> 2. Does the `authentication` block reference it correctly? +> 3. Is the credential still valid (not expired)?" + +**404 / endpoint not found:** +> "The API endpoint returned 404. Verify `entrypointUrl` against the provider's API docs — the path may be wrong or require a different base URL." + +After each fix, re-test the affected action. Repeat until all actions pass. + +--- + +## Step 5: Final summary + +When all actions pass: +> "All tests passed! ✓ +> +> **Connector summary:** +> +> | Resource | Actions | Status | +> |----------|---------|--------| +> | `{{resource1}}` | {{actions1}} | ✓ | +> | `{{resource2}}` | {{actions2}} | ✓ | +> +> **Next steps:** +> 1. Commit your connector: `git add src/configs/{{provider}}/ && git commit -m 'feat: add {{provider}} unified connector'` +> 2. If you'd like StackOne to index this connector, open a pull request to the connectors repository. +> 3. Test end-to-end with a real integration account." + +Save `"tested": true` and `"completed_at": "{{datetime}}"` to session. diff --git a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md new file mode 100644 index 0000000..514f3e4 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md @@ -0,0 +1,114 @@ +--- +name: validate-connector +description: Step 5 of building a unified connector. Runs stackone validate on the connector config, interprets any errors with clear fixes, re-runs until clean, and confirms structural correctness before live testing. +invoke: validate-connector +--- + +# Validate Connector + +Step 5 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Validating connector at `src/configs/{{provider}}/`" + +If `connector_path` is missing: +> "No connector path in session. Run `/check-connector` first." + +Read `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the error table. + +--- + +## Step 1: Run validation + +**If `cli_available` is true:** +```bash +npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.yaml +``` + +**If `cli_available` is false (manual review):** + +Read each `.yaml` and `.partial.yaml` file in `src/configs/{{provider}}/` and check: +- All unified actions have `schemaType: unified` +- All unified actions have a `fieldConfigs` array +- All unified actions have all three steps: `request`, `map_fields` (version 2), `typecast` (version 2) +- `list` actions have a `cursor` block +- `get` actions reference `${inputs.id}` in the URL and have `id` in `inputs` +- No typos in `actionType` values (valid: `custom`, `list`, `get`, `create`, `update`, `delete`) +- All `$ref` paths in the main connector point to files that exist +- No tabs — use 2-space indentation throughout + +--- + +## Step 2: Interpret and fix errors + +For each validation error, explain what it means and show the exact fix. + +**Missing `entrypointUrl`:** +> "The action is missing an endpoint URL. Add `entrypointUrl` pointing to the provider's API path:" +```yaml +entrypointUrl: "/{{resource}}" +entrypointHttpMethod: GET +``` + +**Wrong `actionType`:** +> "The actionType value is invalid. Valid values are: `custom`, `list`, `get`, `create`, `update`, `delete`" + +**Missing `fieldConfigs` on unified action:** +> "Unified actions require a `fieldConfigs` array for field mapping. Run `/map-fields` if you haven't mapped fields yet, or add the array:" +```yaml +fieldConfigs: + - targetFieldKey: id + expression: "$.id" +``` + +**Missing steps on unified action:** +> "Unified actions need all three pipeline steps:" +```yaml +steps: + - type: request + id: fetch + - type: map_fields + version: 2 + id: map + - type: typecast + version: 2 + id: cast +``` + +**Missing `schemaType`:** +> "Add `schemaType: unified` to the action." + +**Bad `$ref` path:** +> "The partial file path in `$ref` doesn't exist. Check the filename matches exactly — naming convention is `{{provider}}.{{resource}}.s1.partial.yaml`" + +After each fix, re-run validation. Repeat until it passes cleanly. + +--- + +## Step 3: Confirm clean validation + +When validation passes (or manual review finds no issues): +> "Validation passed — your connector config is structurally correct. ✓ +> +> **Summary:** +> - Provider: `{{provider}}` +> - Resources: {{resources}} +> - Actions: {{action_scope}} +> - Config path: `src/configs/{{provider}}/`" + +Save `"validated": true` to session. + +--- + +## Handoff + +> "Ready for live testing. +> +> Next step: test your actions against the real `{{provider}}` API. +> Run `/test-connector` to continue. +> +> Before running tests, make sure your `.env` file has the right credentials." + +Update `session_step` to `"test-connector"` in the session file. diff --git a/package-lock.json b/package-lock.json index 6e213d9..98e0a05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@stackone/agent-config": "^1.3.0", "@stackone/cli": "1.16.0", - "@stackone/connect-sdk": "2.28.0", + "@stackone/connect-sdk": "2.31.0", "@stackone/core": "2.3.4", "@stackone/expressions": "0.18.0", "@stackone/utils": "0.14.0" @@ -2852,9 +2852,9 @@ } }, "node_modules/@stackone/connect-sdk": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/@stackone/connect-sdk/-/connect-sdk-2.28.0.tgz", - "integrity": "sha512-WaJO2mtbhCz4YWiRsfXH3ZXeJ/uNbKEBg4W4M3off/i3dYKOreF9NCgzZPC8j+7a7hZkTL+zAfH/3ArXceXDWA==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/@stackone/connect-sdk/-/connect-sdk-2.31.0.tgz", + "integrity": "sha512-uAzZLa5/Iq8LuiyklyphLYCEP1KQWgGbgu1O5nwN1O51cqtB0Ae6xsrtPRAuFbIu3T8HCSzeSZIVdf9ntg1byQ==", "license": "ISC", "dependencies": { "@stackone/core": "*", diff --git a/package.json b/package.json index 760fa97..b472e30 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dependencies": { "@stackone/agent-config": "^1.3.0", "@stackone/cli": "1.16.0", - "@stackone/connect-sdk": "2.28.0", + "@stackone/connect-sdk": "2.31.0", "@stackone/core": "2.3.4", "@stackone/expressions": "0.18.0", "@stackone/utils": "0.14.0" From 15fae396c290544f79faeef83ea93b1fb098ef04 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Thu, 19 Mar 2026 17:19:27 +0000 Subject: [PATCH 02/13] feat: add import-schema skill and wire into choose-schema workflow --- .../skills/choose-schema/SKILL.md | 30 ++- .../skills/import-schema/SKILL.md | 176 ++++++++++++++++++ .../stackone-connector-builder/SKILL.md | 5 +- 3 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 .claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md diff --git a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md index 3310807..20517d8 100644 --- a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md @@ -1,6 +1,6 @@ --- name: choose-schema -description: First step of building a unified connector. Guides the builder to choose or define the output schema — StackOne built-in, custom file, or define inline. Saves schema choice to session file. +description: First step of building a unified connector. Guides the builder to choose or define the output schema — StackOne built-in, import from a document (CSV/JSON/YAML), point to an existing schema file, or define inline. Saves schema choice to session file. invoke: choose-schema --- @@ -33,11 +33,12 @@ Save as `provider` in session (lowercase, hyphenated). Ask: > "What output schema will your connector map data to? Choose one: > -> **A) StackOne built-in schema** — HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, Marketing -> **B) Point to an existing schema file** — You have a JSON/YAML schema in your project -> **C) Define a new custom schema** — I'll help you define fields interactively +> **A) StackOne built-in schema** — HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, Marketing, Accounting +> **B) Import from a schema document** — You have a CSV, JSON, YAML, or spreadsheet that defines your fields +> **C) Point to an existing schema file** — You have a JSON/YAML schema already in connector-builder format +> **D) Define a new custom schema** — I'll help you define fields interactively > -> Which fits your use case? (A/B/C)" +> Which fits your use case? (A/B/C/D)" --- @@ -89,7 +90,22 @@ Save to session: --- -## Path B: Existing schema file +## Path B: Import from schema document + +Execute the full `/import-schema` skill logic inline. + +This handles CSV, JSON, YAML, TSV, and plain field lists. It will: +1. Read the file at the path the builder provides +2. Parse field names and infer types from structure or sample data +3. Flag likely enum fields and collect their allowed values +4. Present the parsed schema for the builder to review and correct +5. Save `schema_fields` to session + +Once `/import-schema` completes, the session will have `schema_source: "imported"` and the full `schema_fields` array. Continue to the **Handoff** section below. + +--- + +## Path C: Existing schema file Ask: > "What is the path to your schema file? (e.g., `schemas/employee.json`)" @@ -118,7 +134,7 @@ Save to session: --- -## Path C: Define custom schema interactively +## Path D: Define custom schema interactively Explain: > "Let's define your schema field by field. For each field I'll ask for: diff --git a/.claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md new file mode 100644 index 0000000..3de2cae --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md @@ -0,0 +1,176 @@ +--- +name: import-schema +description: Converts an end-user's existing schema document (CSV, JSON, YAML, plain text, or spreadsheet headers) into the connector builder's schema_fields format. Use when a builder has an internal schema they want to map to. Saves the result to the session file so /map-fields can use it immediately. +invoke: import-schema +--- + +# Import Schema + +Converts your existing schema document into a format the connector builder understands. + +Supports: **CSV**, **JSON**, **YAML**, **TSV**, **plain text field lists**, and any tabular format where columns or keys represent field names. + +--- + +## Step 1: Ask for the file path + +Ask: +> "What is the path to your schema document? (e.g., `schemas/employee.csv`, `docs/api-schema.json`, `./fields.yaml`)" + +Read the file at the provided path. + +If the file doesn't exist: +> "I couldn't find a file at `{{path}}`. Check the path and try again — or paste the schema content directly and I'll parse it from there." + +--- + +## Step 2: Parse the file by format + +Detect the format from the file extension or content structure. + +### CSV / TSV + +Treat the **first row as field names**. Look at the second row (if present) to determine whether it contains: +- **Type hints** (e.g., `string`, `number`, `date`) → use as types +- **Sample data** (e.g., `John`, `42`, `2024-01-15`) → infer types from the values +- **Nothing** → default all types to `string`, ask the builder to confirm + +Example CSV: +``` +id,first_name,last_name,email,hire_date,status,salary +string,string,string,string,date,enum,number +``` + +→ Parse as 7 fields with explicit types. + +Example CSV with sample data: +``` +id,first_name,last_name,email,hire_date,status,salary +EMP001,John,Smith,john@acme.com,2023-01-15,active,75000 +``` + +→ Infer: `id` string, `first_name` string, `last_name` string, `email` string, `hire_date` date, `status` string (flag as possible enum), `salary` number. + +### JSON + +If it's an **array of objects**, use the keys of the first object as field names and infer types from the values: +```json +[{ "id": "123", "first_name": "Jane", "active": true, "score": 4.5 }] +``` +→ `id` string, `first_name` string, `active` boolean, `score` number. + +If it's a **schema definition object** (keys are field names, values are type strings or objects): +```json +{ + "id": "string", + "hire_date": "date", + "status": { "type": "enum", "values": ["active", "inactive"] } +} +``` +→ Parse directly as typed fields. + +If it's an **OpenAPI-style schema** (has `properties` key): +```json +{ + "properties": { + "id": { "type": "string" }, + "email": { "type": "string", "format": "email" } + }, + "required": ["id", "email"] +} +``` +→ Extract from `properties`, use `required` array. + +### YAML + +Same rules as JSON — detect structure and parse accordingly. + +### Plain text field list + +If the file is a plain list of names (one per line or comma-separated): +``` +id, first_name, last_name, email, hire_date, status +``` +→ Parse as fields, default all types to `string`, then ask the builder to review types. + +--- + +## Step 3: Infer and flag likely enums + +After parsing, flag any field that looks like it might be an enum: +- Field name contains `status`, `type`, `kind`, `category`, `role`, `level`, `state` +- Sample values are short, consistent, and repeat (e.g., `active`, `inactive`, `pending`) + +For flagged fields, ask: +> "These fields look like they might have a fixed set of values. For each one, tell me the allowed values (or `skip` to treat as a plain string): +> +> - `status` — e.g., active, inactive, pending?" + +Collect enum values and add them to the field definition. + +--- + +## Step 4: Present the parsed schema for review + +Show a summary table: + +> "Here's what I extracted from your schema document: +> +> | # | Field | Type | Required | Notes | +> |---|-------|------|---------|-------| +> | 1 | `id` | string | ✓ | | +> | 2 | `first_name` | string | ✓ | | +> | 3 | `status` | enum | | Values: active, inactive | +> | 4 | `hire_date` | date | | | +> | ... | +> +> A few things to check: +> 1. Are the types correct? (string / number / boolean / date / datetime / enum / array / object) +> 2. Which fields are required? +> 3. Any fields I missed or should exclude?" + +Allow the builder to make corrections. Accept natural language like: +- "Make `hire_date` required" +- "Change `salary` type to number" +- "Add `manager_id` as a string" +- "Remove `internal_notes`" +- "`status` values are: active, inactive, on_leave, terminated" + +Apply corrections and show the updated table. Repeat until the builder confirms. + +--- + +## Step 5: Save to session + +Once confirmed, save to `.connector-build-session.json`: + +```json +{ + "schema_source": "imported", + "schema": "custom", + "schema_file": "{{original_file_path}}", + "resources": ["custom"], + "schema_fields": [ + { "name": "id", "type": "string", "required": true }, + { "name": "first_name", "type": "string", "required": true }, + { "name": "status", "type": "enum", "required": false, "enum_values": ["active", "inactive", "on_leave", "terminated"] }, + { "name": "hire_date", "type": "date", "required": false } + ] +} +``` + +--- + +## Handoff + +> "Schema imported. ✓ +> +> **{{N}} fields** loaded from `{{file_path}}` +> +> These fields will be used as your `targetFieldKey` names in `/map-fields`. The next step is deciding which provider operations to build. +> +> - If you haven't set a provider yet: run `/choose-schema` and choose **option B** (it'll pick up your imported schema) +> - If provider + actions are already set: run `/map-fields` directly" + +Update session: +- `session_step` → `"scope-actions"` (if provider is set) or `"choose-schema"` (if not) diff --git a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md index 21e3de8..07d2ff0 100644 --- a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md @@ -14,7 +14,8 @@ Run steps in order, or invoke any sub-skill directly to jump to that phase: | Step | Command | What it does | |------|---------|-------------| -| 1 | `/choose-schema` | Pick or define your output schema | +| 1 | `/choose-schema` | Pick or define your output schema (built-in, import, or define inline) | +| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or spreadsheet | | 2 | `/check-connector` | Check if connector exists, pull or scaffold | | 3 | `/scope-actions` | Decide which resources and operations to expose | | 4 | `/map-fields` | Map provider API fields to your schema | @@ -23,6 +24,8 @@ Run steps in order, or invoke any sub-skill directly to jump to that phase: Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. +`/import-schema` can be run standalone at any point to load schema fields from a document. It feeds directly into `/map-fields`. + --- ## Starting the Wizard From 6c8d0aefc9472e8505177c0fdda71a3ead0da671 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Thu, 19 Mar 2026 17:20:19 +0000 Subject: [PATCH 03/13] fix: collapse redundant schema path C into import-schema (B) --- .../skills/choose-schema/SKILL.md | 42 +++---------------- .../stackone-connector-builder/SKILL.md | 4 +- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md index 20517d8..a58d032 100644 --- a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md @@ -1,6 +1,6 @@ --- name: choose-schema -description: First step of building a unified connector. Guides the builder to choose or define the output schema — StackOne built-in, import from a document (CSV/JSON/YAML), point to an existing schema file, or define inline. Saves schema choice to session file. +description: First step of building a unified connector. Guides the builder to choose or define the output schema — StackOne built-in, import from any schema document (CSV/JSON/YAML), or define inline. Saves schema choice to session file. invoke: choose-schema --- @@ -34,11 +34,10 @@ Ask: > "What output schema will your connector map data to? Choose one: > > **A) StackOne built-in schema** — HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, Marketing, Accounting -> **B) Import from a schema document** — You have a CSV, JSON, YAML, or spreadsheet that defines your fields -> **C) Point to an existing schema file** — You have a JSON/YAML schema already in connector-builder format -> **D) Define a new custom schema** — I'll help you define fields interactively +> **B) Import from a schema document** — Point me to a CSV, JSON, YAML, or any file that defines your fields +> **C) Define a new custom schema** — I'll help you define fields interactively > -> Which fits your use case? (A/B/C/D)" +> Which fits your use case? (A/B/C)" --- @@ -94,7 +93,7 @@ Save to session: Execute the full `/import-schema` skill logic inline. -This handles CSV, JSON, YAML, TSV, and plain field lists. It will: +This handles CSV, JSON, YAML, TSV, plain field lists, and any tabular file. It will: 1. Read the file at the path the builder provides 2. Parse field names and infer types from structure or sample data 3. Flag likely enum fields and collect their allowed values @@ -105,36 +104,7 @@ Once `/import-schema` completes, the session will have `schema_source: "imported --- -## Path C: Existing schema file - -Ask: -> "What is the path to your schema file? (e.g., `schemas/employee.json`)" - -Read the file. Identify the fields: name, type, whether required. - -Present a summary: -> "Found {{N}} fields in your schema: -> - `id` (string, required) -> - `first_name` (string, required) -> - ..." - -Ask: -> "Does this look correct? Any fields I should know are enums or nested objects?" - -Save to session: -```json -{ - "schema_source": "file", - "schema": "custom", - "schema_file": "", - "resources": ["custom"], - "schema_fields": [{ "name": "...", "type": "...", "required": true }] -} -``` - ---- - -## Path D: Define custom schema interactively +## Path C: Define custom schema interactively Explain: > "Let's define your schema field by field. For each field I'll ask for: diff --git a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md index 07d2ff0..f5a5ebc 100644 --- a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md @@ -14,8 +14,8 @@ Run steps in order, or invoke any sub-skill directly to jump to that phase: | Step | Command | What it does | |------|---------|-------------| -| 1 | `/choose-schema` | Pick or define your output schema (built-in, import, or define inline) | -| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or spreadsheet | +| 1 | `/choose-schema` | Pick your schema: built-in (A), import from file (B), or define inline (C) | +| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or any schema document | | 2 | `/check-connector` | Check if connector exists, pull or scaffold | | 3 | `/scope-actions` | Decide which resources and operations to expose | | 4 | `/map-fields` | Map provider API fields to your schema | From 7e5359ab4039effe2963b968eaacd6ae08a35934 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 09:03:47 +0000 Subject: [PATCH 04/13] fix: Making plugins for new users. --- .../.claude-plugin/plugin.json | 4 +- .../stackone-connector-builder/README.md | 84 +- .../references/actions-and-steps.md | 302 +++ .../references/auth-patterns.md | 209 ++ .../references/connector-patterns.md | 535 +++- .../references/field-mapping-guide.md | 202 -- .../skills/build-config/SKILL.md | 151 ++ .../skills/configure-auth/SKILL.md | 88 + .../skills/discover-actions/SKILL.md | 166 ++ .../skills/setup-connector/SKILL.md | 93 + .../stackone-connector-builder/SKILL.md | 140 +- .../skills/test-connector/SKILL.md | 292 +- .../skills/validate-connector/SKILL.md | 234 +- .../.claude-plugin/plugin.json | 11 + .../stackone-unified-builder/README.md | 68 + .../references/actions-and-steps.md | 302 +++ .../references/auth-patterns.md | 209 ++ .../references/cli-commands.md | 74 + .../references/connector-patterns.md | 538 ++++ .../references/field-mapping-guide.md | 314 +++ .../references/schemas/accounting.md | 0 .../references/schemas/ats.md | 0 .../references/schemas/crm.md | 0 .../references/schemas/documents.md | 0 .../references/schemas/hris.md | 0 .../references/schemas/iam.md | 0 .../references/schemas/lms.md | 0 .../references/schemas/marketing.md | 0 .../references/schemas/ticketing.md | 0 .../references/unified-schemas.md | 0 .../skills/check-connector/SKILL.md | 21 + .../skills/choose-schema/SKILL.md | 0 .../skills/import-schema/SKILL.md | 0 .../skills/map-fields/SKILL.md | 20 + .../skills/scope-actions/SKILL.md | 0 .../skills/stackone-unified-builder/SKILL.md | 148 + .../skills/test-connector/SKILL.md | 144 + .../skills/validate-connector/SKILL.md | 114 + .claude/settings.local.json | 14 + .../ashby/ashby.candidates.s1.partial.yaml | 0 .../ashby/ashby.connector.s1.yaml | 0 .../bamboo-hr/bamboo-hr.connector.s1.yaml | 3 +- .../bamboo-hr.employees.s1.partial.yaml | 640 +++++ src/configs/README.md | 792 ------ .../bamboo-hr.employees.s1.partial.yaml | 76 - .../breathehr/breathehr_v1-0-1.s1.yaml | 2411 +++++++++++++++++ 46 files changed, 6916 insertions(+), 1483 deletions(-) create mode 100644 .claude/plugins/stackone-connector-builder/references/actions-and-steps.md create mode 100644 .claude/plugins/stackone-connector-builder/references/auth-patterns.md delete mode 100644 .claude/plugins/stackone-connector-builder/references/field-mapping-guide.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md create mode 100644 .claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md create mode 100644 .claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json create mode 100644 .claude/plugins/stackone-unified-builder/README.md create mode 100644 .claude/plugins/stackone-unified-builder/references/actions-and-steps.md create mode 100644 .claude/plugins/stackone-unified-builder/references/auth-patterns.md create mode 100644 .claude/plugins/stackone-unified-builder/references/cli-commands.md create mode 100644 .claude/plugins/stackone-unified-builder/references/connector-patterns.md create mode 100644 .claude/plugins/stackone-unified-builder/references/field-mapping-guide.md rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/accounting.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/ats.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/crm.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/documents.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/hris.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/iam.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/lms.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/marketing.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/schemas/ticketing.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/references/unified-schemas.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/skills/check-connector/SKILL.md (88%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/skills/choose-schema/SKILL.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/skills/import-schema/SKILL.md (100%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/skills/map-fields/SKILL.md (90%) rename .claude/plugins/{stackone-connector-builder => stackone-unified-builder}/skills/scope-actions/SKILL.md (100%) create mode 100644 .claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md create mode 100644 .claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md create mode 100644 .claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md create mode 100644 .claude/settings.local.json rename {src/configs => examples}/ashby/ashby.candidates.s1.partial.yaml (100%) rename {src/configs => examples}/ashby/ashby.connector.s1.yaml (100%) rename {src/configs => examples}/bamboo-hr/bamboo-hr.connector.s1.yaml (97%) create mode 100644 examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml delete mode 100644 src/configs/README.md delete mode 100644 src/configs/bamboo-hr/bamboo-hr.employees.s1.partial.yaml create mode 100644 src/configs/breathehr/breathehr_v1-0-1.s1.yaml diff --git a/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json b/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json index 885657a..c0ade8e 100644 --- a/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json +++ b/.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json @@ -1,11 +1,11 @@ { "name": "stackone-connector-builder", "version": "1.0.0", - "description": "Interactive step-by-step guide for building unified Falcon connectors. Walks external builders through schema selection, connector scaffolding, action scoping, field mapping, validation, and testing.", + "description": "Interactive wizard for building generic Falcon connectors. Guides builders through provider setup, authentication, action discovery (scoped or maximal), config generation, validation, and testing with full cleanup.", "author": { "name": "StackOne", "email": "engineering@stackone.com" }, "license": "MIT", - "keywords": ["connector", "falcon", "unified", "schema", "stackone", "builder"] + "keywords": ["connector", "falcon", "generic", "stackone", "builder", "discovery"] } diff --git a/.claude/plugins/stackone-connector-builder/README.md b/.claude/plugins/stackone-connector-builder/README.md index 680d08f..daca1ac 100644 --- a/.claude/plugins/stackone-connector-builder/README.md +++ b/.claude/plugins/stackone-connector-builder/README.md @@ -1,68 +1,52 @@ # stackone-connector-builder -Interactive step-by-step wizard for building unified Falcon connectors. Guides external builders through every phase — from choosing an output schema to live API testing. +Claude Code plugin for building generic Falcon connectors that return raw provider API responses. -## Installation +## When to use this plugin -```bash -/plugin install stackone-connector-builder@stackone-claude-marketplace -``` - -## Usage - -### Full wizard (recommended) - -```bash -/build-connector -``` +Use **this plugin** when you want direct API access — actions use `actionType: custom` and return the provider's response as-is, with no schema normalisation. -Walks through all 6 steps in sequence. Progress is saved to `.connector-build-session.json` so you can pause and resume at any time. +Use **`stackone-unified-builder`** (via `/build-unified-connector`) when your connector needs to map provider data to a StackOne unified schema (HRIS, ATS, CRM, etc.). -### Individual steps +## Workflow -Each step can also be run independently. It reads the session file and picks up from the right context: +| Step | Command | What it does | +|------|---------|--------------| +| 1 | `/setup-connector` | Provider setup, checks StackOne index, CLI pull or scaffold | +| 2 | `/configure-auth` | Auth configuration (API key, OAuth2, Basic Auth, custom) | +| 3 | `/discover-actions` | Choose scoped or maximal discovery (MCP-powered) | +| 4 | `/build-config` | Generate YAML with `actionType: custom` for all actions | +| 5 | `/validate-connector` | Validate YAML structure | +| 6 | `/test-connector` | Live test + automatic cleanup of test records | -| Command | Step | What it does | -|---------|------|-------------| -| `/choose-schema` | 1 | Pick a StackOne built-in schema, point to your own, or define one interactively | -| `/check-connector` | 2 | Check if a base connector exists, pull it or scaffold a new one | -| `/scope-actions` | 3 | Choose which resources and CRUD operations to expose | -| `/map-fields` | 4 | Map provider API fields to your schema, writes YAML partials | -| `/validate-connector` | 5 | Validate the YAML config structure | -| `/test-connector` | 6 | Test live against the provider API and verify field output | +## Quick start -### Resume a session - -```bash +``` /build-connector ``` -If a `.connector-build-session.json` exists, the wizard will offer to resume from where you left off. - -## What it builds - -A set of YAML configuration files in `src/configs//` that expose provider API data through StackOne's unified API. Supports both StackOne built-in schemas (HRIS, ATS, CRM, etc.) and custom schemas you define. - -## Schema options +This runs the full workflow from start to finish, guiding you through each step. -| Option | When to use | -|--------|-------------| -| **StackOne built-in** | Your data fits HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, or Marketing | -| **Existing schema file** | You already have a JSON/YAML schema in your project | -| **Define inline** | You want to define fields interactively during the wizard | +## Key features -## Requirements +- **Session persistence** — progress is saved to `.connector-build-session.json` so you can pause and resume at any step +- **Two discovery modes**: + - *Scoped* — describe your use case and relevant endpoints are found via vector search + - *Maximal* — discovers all provider endpoints via MCP (async, 5–15 min); use when you want full coverage +- **Test cleanup** — every record created during testing is tracked and deleted afterwards; any records that cannot be deleted are reported +- **Credential security** — `scramble_credentials` is always called after testing to invalidate live credentials -- `connectors-template` project structure (`src/configs/` directory) -- Node.js — for the StackOne CLI (optional but recommended) -- Provider API credentials in `.env` +## MCP tools used -## How sessions work +| Tool | Purpose | +|------|---------| +| `map_provider_key`, `get_provider_actions` | Check StackOne connector index | +| `discover_actions`, `get_discover_actions_task_status` | Maximal endpoint discovery (async) | +| `analyze_versioning` | Detect API versioning patterns | +| `vector_search` | Scoped discovery — find relevant endpoints by description | +| `test_actions`, `get_test_actions_task_status` | Automated live testing (async) | +| `scramble_credentials` | Invalidate credentials after testing | -Each step reads and writes `.connector-build-session.json` at the project root. This file stores: -- Provider name and schema choice -- CLI availability and connector setup status -- Action scope and known limitations -- Validation and test results +--- -You can safely delete this file to start fresh. +For unified connectors (HRIS/ATS/CRM/etc), see the `stackone-unified-builder` plugin and `/build-unified-connector`. diff --git a/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md b/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md new file mode 100644 index 0000000..4f58396 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md @@ -0,0 +1,302 @@ +# Actions and Steps Reference + +Technical reference for writing action YAML files — covering action structure, inputs, step functions, expressions, and result mapping. + +## Action Structure + +### Required Fields +- `actionId`: Unique identifier +- `categories`: List of categories for StackOne UI +- `actionType`: `custom` (default for non-unified) or `list|get|create|update|delete` (unified only) +- `label`: Human-readable name +- `description`: Short description (shown in UI) +- `steps`: List of step functions +- `result`: Final output + +### Optional Fields +- `details`: Longer description (tool description, rate limits, required permissions) +- `resources`: Action-specific documentation URLs +- `examples`: Input/output demonstration pairs +- `inputs`: Request parameters +- `requiredScopes`: Space-separated OAuth scopes (must be defined in `scopeDefinitions`). Use most restrictive scope. +- `entrypointUrl` / `entrypointHttpMethod`: **Unified actions ONLY** (DO NOT USE for non-unified) + +## Inputs + +For non-unified actions: inputs must match EXACTLY the provider's request parameters. DO NOT create inputs that don't exist in the provider API. Ignore deprecated fields. + +### Supported Types +`string`, `number`, `boolean`, `datetime_string`, `object`, `enum` + +**Never use `type: array`** — always use `array: true` with element type. + +### Input Examples + +**Basic string/number:** +```yaml +inputs: + - name: userId + description: User identifier + type: string + in: path + required: true +``` + +**Object:** +```yaml +inputs: + - name: filter + description: Filter object + type: object + in: body + required: true + properties: + - name: name + description: Filter by name + type: string + required: false +``` + +**Array:** +```yaml +inputs: + - name: userIds + description: Array of user IDs + type: string + array: true + in: body + required: true +``` + +**Enum:** +```yaml +inputs: + - name: status + description: Employment status + type: enum + required: true + in: query + oneOf: + values: + - active + - inactive + - terminated +``` + +### Input locations (`in` field) +- `query` — URL query parameter +- `body` — request body +- `path` — URL path parameter (referenced via `${inputs.fieldName}` in URL) +- `headers` — request header + +## Expression Formats + +### 1. JSONPath (`$.path.to.field`) — PREFERRED +For direct references without string construction: +- Credentials: `$.credentials.apiKey` +- Inputs: `$.inputs.userId` +- Step output: `$.steps.fetch_users.output.data` + +### 2. String Interpolation (`${...}`) +For embedding dynamic values in strings: +- URLs: `/users/${inputs.userId}/posts/${inputs.postId}` +- Domains: `https://${credentials.domain}.api.com` + +### 3. JEXL Expressions (`'{{...}}'`) +For conditional logic, transformations. **Wrap in single quotes.** +- Conditionals: `'{{present(inputs.includeInactive)}}'` +- Ternary: `'{{$.status == "active" ? "enabled" : "disabled"}}'` +- String manipulation: `'{{inputs.name.toUpperCase()}}'` + +**IMPORTANT:** +- For `value` fields: use JSONPath `$.inputs.fieldName` +- For `condition` fields: use JEXL `'{{present(inputs.fieldName)}}'` +- **Never use JEXL for `value` fields** + +## Step Functions + +Every step MUST have a `description` field. + +### `request` — Standard HTTP request + +```yaml +steps: + - stepId: fetch_users + description: List users from the API + stepFunction: + functionName: request + parameters: + url: '/users' + method: get + args: + - name: showInactive + value: $.inputs.showInactive + in: query + condition: '{{present(inputs.showInactive)}}' +``` + +Always use `args` for parameters (never direct `body` field). + +**Custom headers for `authorization.type: none`:** +```yaml +args: + - name: X-API-Key + value: $.credentials.customKey + in: headers +``` + +**Raw array bodies** — when API requires `[...]` instead of `{...}`: +```yaml +args: + - name: events + value: $.inputs.events + in: body + spread: true +``` + +**Custom error mapping:** +```yaml +customErrors: + - receivedStatus: 404 + targetStatus: 400 + message: 'Custom error message' +``` + +### `paginated_request` — Cursor-based pagination + +Only use if provider supports cursor/offset pagination. Otherwise use `request`. + +```yaml +steps: + - stepId: list_records + description: Fetch records with pagination + stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query +``` + +### `soap_request` — SOAP API calls + +```yaml +steps: + - stepId: get_employee + description: Fetch employee via SOAP + stepFunction: + functionName: soap_request + parameters: + url: /EmployeeService + method: post + soapOperation: GetEmployee + soapAction: http://example.com/soap/GetEmployee + useSoapContext: false + namespaces: + - namespaceIdentifier: emp + namespace: http://example.com/employees + args: + - name: EmployeeId + value: ${inputs.employee_id} + in: body +``` + +Key parameters: +- `soapOperation`: SOAP operation name +- `soapAction`: SOAP action URI +- `namespaces`: XML namespace definitions +- `useSoapContext`: Set to `false` when provider expects payload as-is +- Prefix XML attributes with `@_` (e.g., `@_xsi:type`) + +### Other Step Functions +- `group_data`: Groups data from multiple steps +- `map_fields`: Maps using `fieldConfigs` (unified actions only) +- `typecast`: Applies types from `fieldConfigs` (unified actions only) + +## Field Configs (Unified Actions Only) + +NOT required for non-unified connectors. Maps provider response to StackOne unified schema: + +```yaml +fieldConfigs: + - targetFieldKey: id + expression: $.accountId + type: string + - targetFieldKey: type + expression: $.accountType + type: enum + enumMapper: + matcher: + - matchExpression: '{{$.accountType == "atlassian"}}' + value: agent + - matchExpression: '{{$.accountType == "app"}}' + value: bot + - targetFieldKey: active + expression: $.active + type: boolean +``` + +## Result Mapping + +```yaml +# Read response +result: + data: $.steps.fetch_users.output.data + +# Write response +result: + message: Resource updated successfully + data: + id: $.inputs.id +``` + +## GraphQL Patterns + +Reference: `linear` connector + +**Input structure — always use nested `variables` object:** +```yaml +inputs: + - name: variables + description: Variables for the query + type: object + in: body + properties: + - name: first + description: Number of items + type: number + required: false + - name: filter + description: Filter object + type: object + required: false +``` + +**Request:** +```yaml +args: + - name: Content-Type + value: application/json + in: headers + - name: query + value: "query($first: Int) { resources(first: $first) { nodes { id name } } }" + in: body + - name: variables + in: body + condition: "{{present(inputs.variables)}}" + value: + { first: $.inputs.variables.first } +``` + +**IMPORTANT for nested objects:** When querying nested objects, ONLY return the `id` field if a separate action exists to fetch the full object. Don't return full nested objects. + +**Query patterns:** +- List: `query($first: Int, $after: String) { resources(first: $first, after: $after) { nodes { id name } pageInfo { hasNextPage endCursor } } }` +- Get: `query($id: String!) { resource(id: $id) { id name description } }` +- Create: `mutation($input: CreateInput!) { create(input: $input) { success resource { id } } }` +- Update: `mutation($id: String!, $input: UpdateInput!) { update(id: $id, input: $input) { success } }` diff --git a/.claude/plugins/stackone-connector-builder/references/auth-patterns.md b/.claude/plugins/stackone-connector-builder/references/auth-patterns.md new file mode 100644 index 0000000..fe6c04b --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/references/auth-patterns.md @@ -0,0 +1,209 @@ +# Authentication Patterns Reference + +Detailed YAML patterns for every authentication type supported by the Falcon framework. +Used by the `configure-auth` sub-skill. + +## Decision Guide + +- Token exchange via endpoint call? → `type: oauth2` +- No token exchange? → `type: custom` +- Custom headers (not standard Authorization)? → `type: custom` with `authorization.type: none`, inject headers in every action's step args + +## Key Concepts + +- `setupFields` (T1-facing): Multi-tenant/app-level credentials (OAuth Client ID/Secret, scopes) +- `configFields` (T2-facing): End-user specific credentials (API token, username, sub-domain) +- `testActions`: Verify credentials on connection — use action requiring minimal input (e.g., `list_users`) +- `environments`: Define available environments (production, sandbox) +- `support`: Link + description for connection guide + +## Authorization Types (under `custom`) + +| Type | Requires | Use case | +|------|----------|----------| +| `basic` | `username`, `password` | Username + password base64-encoded in Authorization header | +| `bearer` | `token` | Static bearer token in Authorization header | +| `apiKey` | `key`, `value`, `in` (header or query) | API key in a named header or query parameter | +| `none` | — | Inject credentials manually in every action's step args | + +## Referencing Credentials + +- **JSONPath** (for values): `$.credentials.apiKey` +- **String Interpolation** (for URLs): `https://${credentials.subdomain}.api.com` + +--- + +## Custom Auth — Basic Auth + +```yaml +authentication: + - custom: + type: custom + label: API Key + support: + link: https://hub.stackone.com/connection-guides/provider + description: Admin privileges required + authorization: + type: basic + username: $.credentials.email + password: $.credentials.accessToken + configFields: + - key: email + label: Email Address + type: string + required: true + - key: accessToken + label: API Token + type: password + required: true + secret: true + placeholder: ATATT3xFfGF0aQNaJZ... + description: Generate via Account Settings > Security > API tokens + tooltip: Save securely, won't be shown again + environments: + - key: production + name: Production + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — Bearer Token + +```yaml +authentication: + - custom: + type: custom + label: API Key + authorization: + type: bearer + token: $.credentials.apiKey + configFields: + - key: apiKey + label: API Key + type: password + required: true + secret: true + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — API Key Header + +```yaml +authentication: + - custom: + type: custom + label: API Key + authorization: + type: apiKey + key: X-API-Key + value: $.credentials.apiKey + in: header + configFields: + - key: apiKey + label: API Key + type: password + required: true + secret: true + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — Custom Headers (type: none) + +When the API needs non-standard headers, set `authorization.type: none` and pass credentials in EVERY action's step args: + +```yaml +# In connector YAML +authentication: + - custom: + type: custom + label: API Key + authorization: + type: none + configFields: + - key: customKey + label: Custom Header API Key + type: password + required: true + testActions: + - action: list_users + required: true + +# In EVERY partial action step: +stepFunction: + functionName: request + parameters: + url: '/users' + method: get + args: + - name: X-API-Key + value: $.credentials.customKey + in: headers +``` + +--- + +## OAuth 2.0 — Authorization Code + +```yaml +authentication: + - oauth2: + type: oauth2 + label: OAuth 2.0 + grantType: authorization_code + authorizationUrl: https://api.provider.com/oauth/authorize + tokenUrl: https://api.provider.com/oauth/token + setupFields: + - key: clientId + label: Client ID + type: string + required: true + - key: clientSecret + label: Client Secret + type: password + required: true + secret: true + scopes: + - resource.read + - resource.write + testActions: + - action: get_current_user + required: true +``` + +If the refresh token request does not require authorization headers, add: + +```yaml +refreshAuthentication: + authorization: + type: none +``` + +--- + +## Security + +- ALWAYS run `scramble_credentials()` with `securityLevel: PRODUCTION` before storing/committing +- Never use `DEBUG` preset in production +- Supply `customPatterns` for provider-specific credential formats + +--- + +## Common Auth Errors + +| Error | Cause | Fix | +|-------|-------|-----| +| `401 Unauthorized` | Wrong credentials or expired token | Check `.env` values, re-authenticate | +| `403 Forbidden` | Valid credentials, insufficient permissions | Add required scopes or API permissions | +| Literal `$.credentials.apiKey` in request | Interpolation not working | Ensure configFields key matches the reference | +| `base64 is not defined` | Basic auth syntax error | Use the `authorization.type: basic` pattern above | diff --git a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md index 3af6ce7..e484082 100644 --- a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md +++ b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md @@ -1,6 +1,6 @@ # Connector Patterns Reference -YAML patterns for the most common connector configurations. +Official Falcon YAML patterns for connector configurations. Used by `map-fields` and `validate-connector` sub-skills. ## File Structure @@ -11,88 +11,285 @@ src/configs// └── ..s1.partial.yaml # Actions for each resource ``` -## Minimal Connector (API Key / Bearer Token) +## Main Connector File Template ```yaml -name: my-provider -version: "1.0" +StackOne: 1.0.0 +info: + title: Provider Name + key: provider_name + version: 1.0.0 + assets: + icon: https://stackone-logos.com/api/provider_name/filled/png + description: Brief description + details: | + Extended information about this connector + +baseUrl: https://api.provider.com +releaseStage: preview + +rateLimit: + mainRatelimit: 10 + +resources: https://api.provider.com/docs + +scopeDefinitions: + employees.write: + description: Allow writing and modifying employees + includes: employees.read + employees.read: + description: Allow reading employees + authentication: - type: custom - args: - api_key: - value: "${MY_PROVIDER_API_KEY}" - headers: - Authorization: "Bearer ${args.api_key.value}" -baseUrl: "https://api.my-provider.com/v1" + # See auth-patterns.md for full authentication options -actions: [] +actions: + $ref: provider_name.employees + $ref: provider_name.departments ``` -## Connector with OAuth2 +## Partial File Pattern ($ref) + +The main connector references partial files. Each partial starts directly with `- actionId:` — there is **NO `actions:` wrapper** in partial files. +**In the main connector:** ```yaml -name: my-provider -version: "1.0" -authentication: - type: oauth2 - clientId: "${MY_PROVIDER_CLIENT_ID}" - clientSecret: "${MY_PROVIDER_CLIENT_SECRET}" - tokenUrl: "https://auth.my-provider.com/oauth/token" - scopes: - - read:employees - - read:departments -baseUrl: "https://api.my-provider.com/v1" +actions: + $ref: provider_name.employees + $ref: provider_name.departments +``` + +**In `provider_name.employees.s1.partial.yaml`:** +```yaml +- actionId: list_employees + actionType: custom + # ... -actions: [] +- actionId: get_employee + actionType: custom + # ... ``` -## Connector with Basic Auth +--- + +## Non-Unified Actions + +Non-unified actions return the provider's raw response without StackOne schema mapping. +They use `stepFunction`/`functionName`/`parameters`, `stepId`, and `actionId`. +They do NOT use `entrypointUrl`, `entrypointHttpMethod`, or `response:` blocks. + +### Non-Unified Action — List (with pagination) ```yaml -name: my-provider -version: "1.0" -authentication: - type: custom - args: - username: - value: "${MY_PROVIDER_USERNAME}" - password: - value: "${MY_PROVIDER_PASSWORD}" - headers: - Authorization: "Basic ${base64(args.username.value + ':' + args.password.value)}" -baseUrl: "https://api.my-provider.com/v1" +- actionId: list_employees + categories: [employees] + actionType: custom + label: List Employees + description: Retrieve a list of employees + details: | + Fetches all employees with pagination support. Rate limit: 100/min. + resources: https://api.provider.com/docs/employees + examples: + - input: { page: 1 } + output: { data: [{ id: "1", name: "John Doe" }] } + requiredScopes: employees.read + inputs: + - name: page + description: Page number to retrieve + type: number + in: query + required: false + steps: + - stepId: list_employees + description: Fetch employees with pagination + stepFunction: + functionName: paginated_request + parameters: + url: "/employees" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query + result: + data: $.steps.list_employees.output.data +``` + +### Non-Unified Action — Get (single record) -actions: [] +```yaml +- actionId: get_employee + categories: [employees] + actionType: custom + label: Get Employee + description: Retrieve a single employee by ID + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + steps: + - stepId: get_employee + description: Fetch a single employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: get + result: + data: $.steps.get_employee.output.data ``` -## Non-Unified Action (raw provider response) +### Non-Unified Action — Create (POST with body) + +```yaml +- actionId: create_employee + categories: [employees] + actionType: custom + label: Create Employee + description: Create a new employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write + inputs: + - name: first_name + description: Employee first name + type: string + in: body + required: true + - name: last_name + description: Employee last name + type: string + in: body + required: true + - name: email + description: Employee work email + type: string + in: body + required: false + steps: + - stepId: create_employee + description: Create a new employee + stepFunction: + functionName: request + parameters: + url: "/employees" + method: post + args: + - name: firstName + value: $.inputs.first_name + in: body + - name: lastName + value: $.inputs.last_name + in: body + - name: email + value: $.inputs.email + in: body + condition: "{{present(inputs.email)}}" + result: + data: $.steps.create_employee.output.data +``` -Use `actionType: custom` when you want to return the provider's raw response without mapping. +### Non-Unified Action — Update (PATCH/PUT with ID + body) ```yaml -- name: list_employees +- actionId: update_employee + categories: [employees] actionType: custom - entrypointUrl: "/employees" - entrypointHttpMethod: GET + label: Update Employee + description: Update an existing employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write inputs: - page: - type: number + - name: id + description: The employee ID + type: string + in: path + required: true + - name: first_name + description: Updated first name + type: string + in: body required: false - page_size: - type: number + - name: last_name + description: Updated last name + type: string + in: body required: false steps: - - type: request - id: fetch_employees + - stepId: update_employee + description: Update an employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: patch + args: + - name: firstName + value: $.inputs.first_name + in: body + condition: "{{present(inputs.first_name)}}" + - name: lastName + value: $.inputs.last_name + in: body + condition: "{{present(inputs.last_name)}}" + result: + data: $.steps.update_employee.output.data ``` -## Unified Action — list (paginated) +### Non-Unified Action — Delete + +```yaml +- actionId: delete_employee + categories: [employees] + actionType: custom + label: Delete Employee + description: Delete an employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + steps: + - stepId: delete_employee + description: Delete an employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: delete + result: + data: $.steps.delete_employee.output +``` + +--- + +## Unified Actions + +Unified actions map provider data to a StackOne unified schema. +They use `schemaType: unified`, `schema`, `cursor`, `fieldConfigs`, and three steps: `request`, `map_fields` (v2), `typecast` (v2). +Unified actions use `entrypointUrl` and `entrypointHttpMethod`. +Unified actions use a `response:` block inside the request step. + +### Unified Action — List (paginated) ```yaml -- name: unified_list_employees +- actionId: unified_list_employees + categories: [employees] actionType: list schemaType: unified schema: hris/employees + label: List Employees (Unified) + description: Retrieve a unified list of employees + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read entrypointUrl: "/employees" entrypointHttpMethod: GET cursor: @@ -115,102 +312,83 @@ Use `actionType: custom` when you want to return the provider's raw response wit inactive: inactive terminated: inactive steps: - - type: request - id: fetch_employees - - type: map_fields - version: 2 - id: map_employee_fields - - type: typecast - version: 2 - id: cast_types + - stepId: fetch_employees + description: Fetch employees from provider API + stepFunction: + functionName: request + parameters: + url: "/employees" + method: get + response: + dataKey: data + nextKey: meta.next_page + - stepId: map_employee_fields + description: Map provider fields to unified schema + stepFunction: + functionName: map_fields + version: 2 + - stepId: cast_types + description: Cast fields to correct types + stepFunction: + functionName: typecast + version: 2 ``` -## Unified Action — get (single record by ID) +### Unified Action — Get (single record) ```yaml -- name: unified_get_employee +- actionId: unified_get_employee + categories: [employees] actionType: get schemaType: unified schema: hris/employees + label: Get Employee (Unified) + description: Retrieve a single unified employee by ID + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read entrypointUrl: "/employees/${inputs.id}" entrypointHttpMethod: GET inputs: - id: + - name: id + description: The employee ID type: string + in: path required: true fieldConfigs: - targetFieldKey: id expression: "$.id" - targetFieldKey: first_name expression: "$.firstName" + - targetFieldKey: last_name + expression: "$.lastName" steps: - - type: request - id: fetch_employee - - type: map_fields - version: 2 - id: map_fields - - type: typecast - version: 2 - id: typecast -``` - -## Unified Action — create - -```yaml -- name: unified_create_employee - actionType: create - schemaType: unified - schema: hris/employees - entrypointUrl: "/employees" - entrypointHttpMethod: POST - inputs: - first_name: - type: string - required: true - last_name: - type: string - required: true - work_email: - type: string - required: true - steps: - - type: request - id: create_employee - - type: map_fields - version: 2 - id: map_fields - - type: typecast - version: 2 - id: typecast -``` - -## Partial File Pattern ($ref) - -Keep the main connector clean by referencing partial files for each resource: - -```yaml -# in .connector.s1.yaml -actions: - - $ref: "./.employees.s1.partial.yaml" - - $ref: "./.time_off.s1.partial.yaml" - - $ref: "./.departments.s1.partial.yaml" + - stepId: fetch_employee + description: Fetch a single employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: get + response: + dataKey: data + - stepId: map_fields + description: Map provider fields to unified schema + stepFunction: + functionName: map_fields + version: 2 + - stepId: typecast + description: Cast fields to correct types + stepFunction: + functionName: typecast + version: 2 ``` -Each partial contains a raw YAML array of actions: - -```yaml -# in .employees.s1.partial.yaml -- name: unified_list_employees - actionType: list - # ... - -- name: unified_get_employee - actionType: get - # ... -``` +--- ## Pagination Patterns +Used in `paginated_request` for non-unified actions, or `cursor` block for unified actions. + ### Page number (most common) ```yaml cursor: @@ -241,6 +419,109 @@ cursor: type: link_header ``` +### paginated_request iterator patterns (non-unified) + +**Cursor-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query +``` + +**Page-number-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: data + nextKey: meta.next_page + iterator: + key: page + in: query +``` + +**Offset-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: items + totalKey: total + iterator: + key: offset + in: query + step: 100 +``` + +**Link header:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: data + paginationType: link_header +``` + +--- + +## Result Mapping + +### Read actions (list/get) +```yaml +result: + data: $.steps..output.data +``` + +### Write actions (create/update) +```yaml +result: + data: $.steps..output.data +``` + +### Delete actions +```yaml +result: + data: $.steps..output +``` + +--- + +## Key Distinctions Summary + +| Feature | Non-Unified | Unified | +|---|---|---| +| `actionType` | `custom` | `list`, `get`, `create`, etc. | +| `schemaType` | not present | `unified` | +| `schema` | not present | e.g. `hris/employees` | +| `entrypointUrl` | **NOT used** | required | +| `entrypointHttpMethod` | **NOT used** | required | +| `response:` in request step | **NOT used** | required | +| `cursor` block | not present | present for list actions | +| `fieldConfigs` | not present | required | +| Steps | `request` only | `request` + `map_fields` v2 + `typecast` v2 | +| Step structure | `stepId` + `stepFunction` | `stepId` + `stepFunction` | + +**Important v2.2.0 breaking change:** Use `resources` (NOT `context`) for documentation links at both the connector and action level. + +--- + ## Auth Credential Variable Naming | Auth Type | Variable Pattern | Example | @@ -251,5 +532,7 @@ cursor: | Username | `${PROVIDER_USERNAME}` | `${WORKDAY_USERNAME}` | | Password | `${PROVIDER_PASSWORD}` | `${WORKDAY_PASSWORD}` | | Base URL | `${PROVIDER_BASE_URL}` | `${WORKDAY_BASE_URL}` | +| Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | +| Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | -Reference inside the connector using `${args..value}` after defining in the `args` block. +Reference inside the connector using `${args..value}` after defining in the authentication `args` block. diff --git a/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md b/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md deleted file mode 100644 index f5010df..0000000 --- a/.claude/plugins/stackone-connector-builder/references/field-mapping-guide.md +++ /dev/null @@ -1,202 +0,0 @@ -````markdown -# Field Mapping Guide - -Detailed reference for writing `fieldConfigs` in unified connectors. -Used by the `map-fields` sub-skill. - -## The Golden Rule - -`targetFieldKey` is ALWAYS your schema field name — NEVER the provider's field name. - -```yaml -# CORRECT — targetFieldKey uses your schema name -- targetFieldKey: first_name - expression: "$.firstName" - -# WRONG — targetFieldKey uses the provider's name -- targetFieldKey: firstName - expression: "$.firstName" -``` - ---- - -## Expression Types - -### Direct JSONPath -```yaml -- targetFieldKey: id - expression: "$.id" - -- targetFieldKey: first_name - expression: "$.firstName" -``` - -### Nested path -```yaml -- targetFieldKey: department_name - expression: "$.department.name" - -- targetFieldKey: work_email - expression: "$.contact.emails[0].value" -``` - -### JEXL expression (computed or conditional) - -Wrap JEXL in single-quotes with double-curly-brace syntax: - -```yaml -# Combine two fields into one -- targetFieldKey: display_name - expression: "'{{$.firstName}} {{$.lastName}}'" - -# Fallback — use second value if first is null/undefined -- targetFieldKey: email - expression: "'{{$.workEmail || $.personalEmail}}'" - -# Conditional -- targetFieldKey: is_active - expression: "'{{$.status === \"active\" ? true : false}}'" -``` - -### Array of primitive values -```yaml -- targetFieldKey: tags - expression: "$.tags" - array: true -``` - ---- - -## Enum Mapping - -Translate provider-specific values to your schema's enum values: - -```yaml -- targetFieldKey: employment_status - expression: "$.status" - enumMapper: - # provider_value: schema_value - active: active - inactive: inactive - terminated: inactive - on_leave: leave - pending_hire: pending -``` - -**Important:** Keys are case-sensitive and must match the provider's exact values. - -For complex logic, use `matchExpression` (JEXL): -```yaml -- targetFieldKey: gender - expression: "$.gender" - enumMapper: - matchExpression: "'{{$.gender === \"M\" ? \"male\" : $.gender === \"F\" ? \"female\" : \"other\"}}'" -``` - ---- - -## Nested Object Mapping - -For schema fields that are objects (e.g., `department: { id, name }`): - -```yaml -- targetFieldKey: department - objectMapping: - id: - expression: "$.departmentId" - name: - expression: "$.departmentName" -``` - -If the provider returns the nested object directly: -```yaml -- targetFieldKey: department - expression: "$.department" - objectMapping: - id: - expression: "$.id" - name: - expression: "$.name" -``` - ---- - -## Array of Objects Mapping - -For schema fields that are arrays of objects (e.g., `phone_numbers: [{ type, value }]`): - -```yaml -- targetFieldKey: phone_numbers - array: true - expression: "$.phones" - objectMapping: - type: - expression: "$.phoneType" - value: - expression: "$.phoneNumber" -``` - ---- - -## Common Provider-to-Schema Field Patterns - -| Provider Pattern | Schema Field | Expression | Notes | -|----------------|-------------|-----------|-------| -| `firstName` | `first_name` | `$.firstName` | camelCase → snake_case | -| `first_name` | `first_name` | `$.first_name` | already snake_case | -| `properties.email` | `work_email` | `$.properties.email` | HubSpot-style nested | -| `emails[0]` | `work_email` | `$.emails[0]` | First item of array | -| `contact.phone` | `phone` | `$.contact.phone` | Nested path | -| Unix timestamp | `created_at` | `$.created_at` | typecast handles conversion | -| ISO date string | `hire_date` | `$.hireDate` | typecast handles conversion | -| `department.id` + `department.name` | `department` | objectMapping pattern | | - ---- - -## Required Step Functions for Unified Actions - -All unified actions MUST include all three steps in this exact order: - -```yaml -steps: - - type: request # 1. Fetch data from provider API - id: fetch_data - - - type: map_fields # 2. Apply fieldConfigs transformations - version: 2 - id: map_fields - - - type: typecast # 3. Convert types to match schema (dates, numbers, etc.) - version: 2 - id: typecast -``` - -Non-unified (`actionType: custom`) actions only need the `request` step. - ---- - -## Debugging Field Mapping Issues - -### See the raw provider response -```bash -npx @stackone/cli test --debug -``` -This shows exactly what the provider returns before any mapping. Use it to find the correct JSONPath. - -### Field is null in output -1. Check the JSONPath against the raw response — is the field actually there? -2. Is the field nested? Try `$.nested.field` or `$.items[0].field` -3. Does the provider only return this field sometimes? Handle with JEXL fallback. - -### Enum value passes through unmapped -- Check that the `enumMapper` key matches the provider's exact value (case-sensitive) -- Use `--debug` to see the raw enum value coming from the provider - -### Date field is wrong format -- Ensure `typecast` step is present — it handles date/datetime conversions -- Ensure the schema field type is `date` or `datetime`, not `string` - -### Nested object is empty -- Verify `objectMapping` is at the same level as `targetFieldKey` -- Check the expression points to the right parent object in the raw response -```` diff --git a/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md new file mode 100644 index 0000000..8fef192 --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md @@ -0,0 +1,151 @@ +--- +name: build-config +description: Step 4 of building a generic Falcon connector. Generates YAML action configurations for all confirmed actions using actionType custom, writes partial files per resource, and updates the main connector YAML with $ref links. +invoke: build-config +--- + +# Build Config + +Step 4 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Building config for `{{provider}}` — {{N}} actions across {{M}} resources." + +Read `${CLAUDE_PLUGIN_ROOT}/references/connector-patterns.md` before writing any YAML. + +If `action_scope` is missing: +> "No action scope found. Run `/discover-actions` first." + +--- + +## Step 1: Confirm endpoint details + +If endpoint URLs weren't captured during discovery, ask for each resource: +> "What is the API endpoint path for `{{resource}}`? (e.g., `/employees`, `/v2/employees`)" + +Use any data from `get_provider_actions` or the builder's API docs. + +--- + +## Step 2: Write partial YAML files + +Create `src/configs/{{provider}}/{{provider}}.{{resource}}.s1.partial.yaml` for each resource. + +### list action +```yaml +- name: list_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}" + entrypointHttpMethod: GET + inputs: + page: + type: number + required: false + per_page: + type: number + required: false + steps: + - type: request + id: fetch_{{resource}} +``` + +### get action +```yaml +- name: get_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: GET + inputs: + id: + type: string + required: true + steps: + - type: request + id: fetch_{{resource}} +``` + +### create action +```yaml +- name: create_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}" + entrypointHttpMethod: POST + inputs: + body: + type: object + required: true + steps: + - type: request + id: create_{{resource}} +``` + +### update action +```yaml +- name: update_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: PATCH + inputs: + id: + type: string + required: true + body: + type: object + required: true + steps: + - type: request + id: update_{{resource}} +``` + +### delete action +```yaml +- name: delete_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: DELETE + inputs: + id: + type: string + required: true + steps: + - type: request + id: delete_{{resource}} +``` + +--- + +## Step 3: Update main connector YAML with $refs + +Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` and add a `$ref` per partial under `actions`: + +```yaml +actions: + - $ref: "./{{provider}}.employees.s1.partial.yaml" + - $ref: "./{{provider}}.departments.s1.partial.yaml" +``` + +--- + +## Step 4: Review with builder + +> "Config written: +> - `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` (updated) +> - `src/configs/{{provider}}/{{provider}}.employees.s1.partial.yaml` +> - ... +> +> Do the endpoint URLs and HTTP methods look right? Any actions need adjusting?" + +Apply corrections before moving on. + +--- + +## Handoff + +> "Config built. ✓ +> +> Next: validate the YAML. +> Run `/validate-connector` to continue." + +Update `session_step` to `"validate-connector"`. diff --git a/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md new file mode 100644 index 0000000..bba605b --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md @@ -0,0 +1,88 @@ +--- +name: configure-auth +description: Step 2 of building a generic Falcon connector. Asks which authentication type the provider uses, generates the correct YAML auth block from patterns, and writes it into the connector config file. +invoke: configure-auth +--- + +# Configure Auth + +Step 2 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Configuring auth for `{{provider}}`." + +Read `${CLAUDE_PLUGIN_ROOT}/references/auth-patterns.md` before starting. + +--- + +## Step 1: Identify auth type + +Ask: +> "How does `{{provider}}`'s API authenticate requests? +> +> **A) API Key / Bearer token** — static key passed in a header +> **B) OAuth 2.0** — users authorise via a browser flow +> **C) Basic Auth** — username and password +> **D) API Key in query parameter** — key appended to the URL +> **E) Something else** — describe it and I'll help figure it out +> +> (A/B/C/D/E)" + +--- + +## Step 2: Collect credential details + +Ask for the specific values needed: + +- **A (Bearer):** "What header does `{{provider}}` expect? (e.g., `Authorization: Bearer ...`, `X-API-Key: ...`)" +- **B (OAuth2):** "Do you have the token URL and authorization URL? What scopes are needed?" +- **C (Basic Auth):** "Is this username + password, or username + API key as the password?" +- **D (Query param):** "What is the query parameter name? (e.g., `api_key`, `token`)" +- **E (Other):** Gather details and match to closest pattern in `auth-patterns.md` + +--- + +## Step 3: Write auth block into connector YAML + +Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. + +Replace the `authentication: {}` placeholder with the correct pattern from `auth-patterns.md`. + +Also set `baseUrl`: +> "What is the base URL for `{{provider}}`'s API? (e.g., `https://api.provider.com/v1`)" + +If unknown, use placeholder: `baseUrl: "# TODO: set provider base URL"`. + +--- + +## Step 4: Show required .env variables + +Tell the builder exactly what to add to `.env`: +> "Add these to your `.env` file before testing: +> ``` +> {{PROVIDER_CREDENTIAL_VARS}} +> ```" + +--- + +## Step 5: Confirm + +Show the written auth block and ask: +> "Does this look right? The credentials stay in `.env` — the YAML only references them by variable name." + +Apply any corrections. + +Save `auth_type` to session. + +--- + +## Handoff + +> "Auth configured. ✓ +> +> Next: choose which actions to build. +> Run `/discover-actions` to continue." + +Update `session_step` to `"discover-actions"`. diff --git a/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md new file mode 100644 index 0000000..3d8d93f --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md @@ -0,0 +1,166 @@ +--- +name: discover-actions +description: Step 3 of building a generic Falcon connector. Lets the builder choose between scoped discovery (use-case driven with MCP vector search) or maximal discovery (finds every available API endpoint using the StackOne discover_actions MCP tool asynchronously). Saves confirmed action scope to session. +invoke: discover-actions +--- + +# Discover Actions + +Step 3 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Discovering actions for `{{provider}}`." + +--- + +## Step 1: Choose discovery mode + +Ask: +> "How much of `{{provider}}`'s API do you want to cover? +> +> **A) Scoped** — Tell me your use case and I'll recommend the right actions +> **B) Maximal** — Discover every available endpoint (takes 5–15 minutes, uses autonomous research) +> +> (A/B)" + +Save `discovery_mode: "scoped"` or `"maximal"` to session. + +--- + +## Path A: Scoped discovery + +### A1: Ask about use case + +Ask: +> "Describe what you need this connector to do. Examples: +> - "Read employee and department data into our platform" +> - "Let users create and update time-off requests" +> - "Sync job postings to our job board" +> +> What's your use case?" + +Save `use_case` to session. + +### A2: Look up relevant actions + +``` +vector_search("{{use_case}} {{provider}}") +``` + +``` +get_provider_actions("{{provider_key}}") +``` + +### A3: Present recommended action set + +> "Based on your use case, here are the actions I recommend: +> +> | Resource | Actions | Endpoints | +> |----------|---------|----------| +> | `employees` | list, get | `GET /employees`, `GET /employees/{id}` | +> | `departments` | list | `GET /departments` | +> +> Does this cover what you need? You can add or remove anything." + +Allow the builder to adjust freely. + +### A4: Flag known limitations + +If `get_provider_actions` returned coverage data, note any gaps: +> "Note: `{{provider}}` doesn't appear to support `create` on employees via API — only `list` and `get` are confirmed." + +### A5: Save to session + +```json +{ + "discovery_mode": "scoped", + "action_scope": { + "employees": ["list", "get"], + "departments": ["list"] + }, + "use_case": "Read employee and department data" +} +``` + +--- + +## Path B: Maximal discovery + +### B1: Check S3 cache first + +``` +map_provider_key("{{provider}}") +get_provider_actions("{{provider_key}}") +``` + +If cached data exists with many actions: +> "Found {{N}} actions in the StackOne index for `{{provider}}`. I can use this directly — no need to wait for a full discovery run." + +Use cached data and skip to B4. + +### B2: Launch autonomous discovery + +``` +discover_actions({ provider: "{{provider}}", maxIterations: 30 }) +``` + +Returns a `taskId` immediately. Tell the builder: +> "Launched autonomous discovery for `{{provider}}`. This typically takes 5–15 minutes. I'll poll for results every 60–90 seconds." + +### B3: Poll for completion + +Every 60–90 seconds: +``` +get_discover_actions_task_status("{{taskId}}", "{{provider}}") +``` + +Status progression: `pending` → `running` → `complete` + +When `running`, report progress: +> "Still discovering... found {{N}} actions so far." + +### B4: Run version analysis + +Once discovery completes, extract all endpoints and run: +``` +analyze_versioning({ provider: "{{provider}}", endpoints: [...], maxIterations: 5 }) +``` + +Poll with `get_analyze_versioning_task_status`. Identifies deprecated endpoints and version conflicts. + +### B5: Present full action list + +> "Discovery complete. Found {{N}} actions across {{M}} resources: +> +> | Resource | Actions found | +> |----------|-------------| +> | `employees` | list, get, create, update | +> | `departments` | list, get | +> | `time_off` | list, get, create, delete | +> +> Which would you like to include? (all / select specific ones)" + +### B6: Save to session + +```json +{ + "discovery_mode": "maximal", + "action_scope": { "...all confirmed actions..." }, + "use_case": "Full API coverage" +} +``` + +--- + +## Handoff + +> "Action scope confirmed. ✓ +> +> {{N}} actions across {{M}} resources. +> +> Next: generate the connector YAML. +> Run `/build-config` to continue." + +Update `session_step` to `"build-config"`. diff --git a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md new file mode 100644 index 0000000..5e7fdce --- /dev/null +++ b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md @@ -0,0 +1,93 @@ +--- +name: setup-connector +description: Step 1 of building a generic Falcon connector. Checks StackOne's provider index via MCP, detects CLI availability, and either pulls an existing connector base config or scaffolds a new one. +invoke: setup-connector +--- + +# Setup Connector + +Step 1 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. If `provider` is already set: +> "Resuming — provider already set to `{{provider}}`. Run `/configure-auth` to continue." + +--- + +## Step 1: Ask for provider name + +Ask: +> "What provider are you building a connector for? (e.g., `workday`, `bamboohr`, `rippling`)" + +Save as `provider` (lowercase, hyphenated). + +--- + +## Step 2: Check StackOne provider index + +``` +map_provider_key("{{provider}}") +``` + +If a key is returned, save as `provider_key` in session. + +``` +get_provider_actions("{{provider_key}}") +``` + +If indexed actions exist: +> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference." + +If not found: +> "`{{provider}}` isn't indexed yet — we'll discover actions from scratch in Step 3." + +--- + +## Step 3: Detect CLI availability + +```bash +npx @stackone/cli --version +``` + +Save `cli_available: true` or `false` to session. + +If unavailable: +> "StackOne CLI isn't available. I'll guide manual scaffolding instead." + +--- + +## Step 4: Pull or scaffold + +**If CLI available and provider exists in StackOne index:** +```bash +npx @stackone/cli pull {{provider}} +``` +Verify `src/configs/{{provider}}/` was created. Save `connector_exists: true`. + +**If not in index or scaffolding fresh:** +```bash +npx @stackone/cli scaffold {{provider}} +``` +Or if CLI unavailable, create `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` manually: +```yaml +name: {{provider}} +version: "1.0" +authentication: {} # configured in next step +baseUrl: "" # set in next step +actions: [] +``` +Save `connector_exists: false`. + +Save `connector_path: "src/configs/{{provider}}"` to session. + +--- + +## Handoff + +> "Setup complete. ✓ Connector at `src/configs/{{provider}}/` +> +> Next: configure authentication. +> Run `/configure-auth` to continue." + +Update `session_step` to `"configure-auth"`. diff --git a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md index f5a5ebc..d7963bc 100644 --- a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md @@ -1,30 +1,27 @@ --- name: stackone-connector-builder -description: Interactive wizard for building a unified Falcon connector from scratch. Guides external builders through schema selection, connector setup, action scoping, field mapping, validation, and testing. Auto-triggers when someone asks to build a new connector, integrate with a new provider, or create a unified connector. +description: Interactive wizard for building a generic Falcon connector. Guides builders through provider setup, authentication, action discovery (scoped or maximal), YAML config generation, validation, and live testing with cleanup. Auto-triggers when someone asks to build a new connector, add a new provider, or create a Falcon config. invoke: build-connector --- # Build Connector -End-to-end wizard for building a unified StackOne Falcon connector. +End-to-end wizard for building a generic StackOne Falcon connector. +Use this when you want to expose a provider's raw API responses — no schema mapping required. +For connectors that normalise data to a standard schema, use `/build-unified-connector` instead. ## Quick Reference -Run steps in order, or invoke any sub-skill directly to jump to that phase: - | Step | Command | What it does | |------|---------|-------------| -| 1 | `/choose-schema` | Pick your schema: built-in (A), import from file (B), or define inline (C) | -| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or any schema document | -| 2 | `/check-connector` | Check if connector exists, pull or scaffold | -| 3 | `/scope-actions` | Decide which resources and operations to expose | -| 4 | `/map-fields` | Map provider API fields to your schema | -| 5 | `/validate-connector` | Validate the YAML configuration | -| 6 | `/test-connector` | Test live against the provider API | - -Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. +| 1 | `/setup-connector` | Provider name, index check, CLI pull or scaffold | +| 2 | `/configure-auth` | Set up authentication (API key, OAuth2, Basic Auth) | +| 3 | `/discover-actions` | Choose scoped actions or discover everything | +| 4 | `/build-config` | Generate YAML for all confirmed actions | +| 5 | `/validate-connector` | Validate the YAML config | +| 6 | `/test-connector` | Live test + clean up all test records | -`/import-schema` can be run standalone at any point to load schema fields from a document. It feeds directly into `/map-fields`. +Progress is saved to `.connector-build-session.json` — pause and resume any time. --- @@ -33,107 +30,63 @@ Each step saves progress to `.connector-build-session.json` — you can pause an Check for an existing `.connector-build-session.json`. If found: > "Found an existing session: > - **Provider:** `{{provider}}` -> - **Schema:** `{{schema}}` -> - **Last step completed:** `{{session_step}}` +> - **Last step:** `{{session_step}}` > -> Would you like to: -> - **Resume** from `{{session_step}}` -> - **Restart** from the beginning (clears session)" +> Resume or restart?" -If no session exists, greet the builder and proceed to Step 1: -> "Welcome to the StackOne connector builder. I'll guide you through building a unified Falcon connector step by step. +If no session, greet: +> "Welcome to the StackOne connector builder. I'll help you build a Falcon connector that exposes `{{provider}}`'s API through StackOne. > -> You can run `/build-connector` to go through the full flow, or jump to any individual step with `/choose-schema`, `/check-connector`, `/scope-actions`, `/map-fields`, `/validate-connector`, or `/test-connector`." - ---- - -## Step 1 — Choose Schema - -Execute the full `/choose-schema` skill logic. - -**Outcome saved to session:** -- `provider` — the provider name (e.g., `bamboohr`) -- `schema` — the target category or `custom` -- `schema_source` — `builtin`, `file`, or `custom` -- `resources` — list of resources to build -- `schema_fields` — field definitions (for custom/file schemas) - ---- - -## Step 2 — Check Connector - -Execute the full `/check-connector` skill logic. - -**Outcome saved to session:** -- `cli_available` — whether the StackOne CLI is usable -- `connector_exists` — whether a base config was pulled from the index -- `connector_path` — where the config lives -- `auth_type` — the authentication method +> If you want to map the data to a standard schema (HRIS, ATS, CRM, etc.), use `/build-unified-connector` instead." --- -## Step 3 — Scope Actions +## Steps -Execute the full `/scope-actions` skill logic. +### Step 1 — Setup +Execute `/setup-connector` logic. Saves: `provider`, `provider_key`, `cli_available`, `connector_exists`, `connector_path`. -**Outcome saved to session:** -- `action_scope` — `{ resource: [actions] }` map -- `use_case` — builder's description of their goal -- `known_limitations` — any flagged gaps +### Step 2 — Configure Auth +Execute `/configure-auth` logic. Saves: `auth_type`. Writes auth block to connector YAML. ---- - -## Step 4 — Map Fields - -Execute the full `/map-fields` skill logic. - -**Outcome:** Partial YAML files written to `src/configs/{{provider}}/` - ---- +### Step 3 — Discover Actions +Execute `/discover-actions` logic. Saves: `discovery_mode`, `action_scope`, `use_case`. -## Step 5 — Validate +### Step 4 — Build Config +Execute `/build-config` logic. Writes action YAML to `src/configs/{{provider}}/`. -Execute the full `/validate-connector` skill logic. +### Step 5 — Validate +Execute `/validate-connector` logic. Saves: `validated: true`. -**Outcome saved to session:** -- `validated: true` - ---- - -## Step 6 — Test - -Execute the full `/test-connector` skill logic. - -**Outcome saved to session:** -- `tested: true` -- `completed_at` +### Step 6 — Test +Execute `/test-connector` logic. Saves: `tested: true`, `test_artifacts`, `completed_at`. --- ## Session File Schema -`.connector-build-session.json` lives at the project root and carries all state between steps: - ```json { - "provider": "bamboohr", - "schema": "hris", - "schema_source": "builtin", - "schema_fields": [], - "resources": ["employees", "time_off"], + "provider": "workday", + "provider_key": "workday", + "connector_path": "src/configs/workday", "cli_available": true, "connector_exists": false, - "connector_path": "src/configs/bamboohr", - "auth_type": "api_key", + "auth_type": "oauth2", + "discovery_mode": "scoped", "action_scope": { - "employees": ["list", "get"], - "time_off": ["list", "get", "create"] + "employees": ["list", "get", "create"], + "departments": ["list", "get"] }, - "use_case": "Sync HR data into customer's internal system", + "use_case": "Read employee and department data", "known_limitations": [], + "test_artifacts": [ + { "resource": "employees", "id": "EMP_test_001", "cleaned_up": true }, + { "resource": "departments", "id": "DEP_test_007", "cleaned_up": false, "reason": "no delete endpoint" } + ], "validated": false, "tested": false, - "session_step": "map-fields", + "session_step": "build-config", "completed_at": null } ``` @@ -142,7 +95,8 @@ Execute the full `/test-connector` skill logic. ## Rules -- Do not write YAML files until Step 4 (`map-fields`) -- Do not run live tests until Step 5 (`validate-connector`) has passed -- Do not delete the session file unless the builder explicitly requests a reset -- Always read session context at the start of each step — never ask the builder for information already in the session +- Do not write YAML until Step 4 (`build-config`) +- Do not run tests until Step 5 (`validate-connector`) passes +- Always clean up test records — log anything that cannot be removed +- Always run `scramble_credentials` after testing +- Never ask for information already in the session diff --git a/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md index 9f38150..f9583d0 100644 --- a/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md @@ -1,130 +1,262 @@ --- name: test-connector -description: Step 6 (final) of building a unified connector. Runs live action tests against the provider API, verifies field mapping output matches the schema, and surfaces failures with actionable fix guidance. +description: Step 6 (final) of building a generic Falcon connector. Tests all actions live, tracks and deletes every test record created, prints a cleanup report, and scrambles credentials when done. invoke: test-connector --- -# Test Connector +# Step 6: Test Connector -Step 6 — the final step of the unified connector build process. +You are on the final step of building a generic Falcon connector. This step runs live tests against the provider API, tracks every record created, cleans up all test artifacts, and scrambles credentials when done. -## Session File +**Generic connectors use `actionType: custom` and return raw provider responses — no schema mapping.** -Read `.connector-build-session.json`. +--- + +## Session Check + +Read `.connector-build-session.json` from the project root. -If `validated` is not `true`, warn: -> "The connector hasn't been validated yet. Run `/validate-connector` first — it catches structural issues before you spend time on live tests." +If `validated` is not `true`, warn the builder: -If `cli_available` is `false`: -> "The StackOne CLI isn't available, so I can't run automated tests. I'll guide you through manual verification instead." +> Warning: The session does not show this connector has been validated. It is strongly recommended to run `/validate-connector` before running live tests. You can continue, but validation errors may cause test failures. + +Extract from the session: +- `provider` — the provider slug (e.g. `smartrecruiters`) +- `action_scope` — the list of resources and their actions to test +- `test_artifacts` — existing array (initialize to `[]` if missing) --- -## Step 1: Credentials check +## Step 1: Credentials Check + +Before running any tests, confirm the builder has their credentials ready. -Ask before running anything: -> "Do you have credentials for `{{provider}}` ready in your `.env` file? +Ask the builder: + +> Before we run live tests, confirm that your `.env` file is set up with the credentials for `{{provider}}`. > -> You'll need a line like: -> ``` -> {{PROVIDER_CREDENTIAL_NAME}}=your_value_here -> ``` +> Check the `authentication` block in your connector YAML for the exact environment variable name(s) required. For example, if the YAML shows `apiKey: ${{env.SMARTRECRUITERS_API_KEY}}`, make sure `SMARTRECRUITERS_API_KEY` is set in your `.env`. > -> The exact variable name is in `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` — look in the `authentication` block for the `args` key names." +> Reply "ready" when your credentials are in place, or share any issues and we can troubleshoot first. -Wait for confirmation before proceeding. +Wait for the builder to confirm before proceeding. --- -## Step 2: Test each action +## Step 2: Test Each Action + +Work through every resource in `action_scope` in this order for each resource: + +``` +list → get → create → update → delete +``` + +Only test actions that are present in `action_scope` for that resource. Skip any action not listed. + +### MCP Path (preferred) + +Use the `test_actions` MCP tool: + +```json +test_actions({ + "provider": "{{provider}}", + "actions": ["list_{{resource}}", "get_{{resource}}", "create_{{resource}}", "update_{{resource}}", "delete_{{resource}}"] +}) +``` + +This returns a `taskId`. Poll `get_test_actions_task_status(taskId)` every few seconds until `status` is `"completed"` or `"failed"`. Retrieve the results from the completed task response. + +Run one resource at a time so you can track artifacts and handle cleanup per resource before moving to the next. -Work through each resource and action in `action_scope`. Test in this order: `list` → `get` → `create` → `update` → `delete`. +### CLI Fallback + +If MCP tools are unavailable, run each action individually: -**If CLI is available:** ```bash -npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} +npx @stackone/cli test {{provider}} {{action}} ``` -**If CLI is not available:** -Ask the builder to call the provider API manually and paste the response. Manually verify the `fieldConfigs` expressions against the pasted response. +For example: +```bash +npx @stackone/cli test smartrecruiters list_jobs +npx @stackone/cli test smartrecruiters create_job +``` + +Add `--debug` to see the raw request and response if needed. --- -## Step 3: Verify output for each action +## Step 3: Verify Output + +After each action completes, verify the response matches expectations: -**For `list` actions:** -- Does the response contain a `data` array? -- Does each item in `data` have all required schema fields populated (non-null)? -- If the provider has multiple pages — is there a `next_cursor` or `next_page` token in the response? -- Are there any unexpected null fields that should have values? +**`list` actions:** +- Response contains an array of items (even if empty is acceptable, confirm with builder) +- If the provider paginates, check that pagination fields are present (e.g. `next_page`, `cursor`, `offset`, `total`) +- Note the ID of the first item — you may need it for the `get` test -**For `get` actions:** -- Single object returned with the correct schema shape? -- Does the `id` field match what was requested? +**`get` actions:** +- Response is a single object (not an array) +- The returned ID matches the ID requested -**For `create` actions:** -- Record created successfully? -- Does the response contain the new record's `id`? +**`create` actions:** +- Response contains a new record ID +- **Immediately record this ID** in `test_artifacts` (see Step 4) -**For `update` actions:** -- Changes reflected in the response? +**`update` actions:** +- Response reflects the changes that were sent in the request body (or returns 200/204 with no body — check the provider docs) -**For `delete` actions:** -- Returns success (200 or 204)? +**`delete` actions:** +- Response is HTTP 200 or 204 +- **Immediately confirm cleanup** with a follow-up `get` (see Step 4) + +If any verification fails, go to Step 5 (diagnose and fix) before continuing. --- -## Step 4: Diagnose and fix failures +## Step 4: Cleanup (CRITICAL) + +**Every test record created must be tracked and deleted. The goal is to leave zero trace on the provider system.** + +### Tracking Created Records + +Immediately after every successful `create` response, add an entry to `test_artifacts` in the session: + +```json +{ + "resource": "{{resource}}", + "id": "{{new_record_id}}", + "cleaned_up": false +} +``` + +Write the updated `test_artifacts` array back to `.connector-build-session.json` right away — do not wait until the end. + +### Deleting Test Records + +After the `update` step for a resource is verified, run `delete` on the test record created during `create`: + +- If `delete_{{resource}}` is in `action_scope`: run it targeting the created record's ID +- After delete: run `get_{{resource}}` for that ID and confirm a 404 or empty response +- Mark the artifact as `cleaned_up: true` in the session file + +### When Delete Is Not Available + +If `delete_{{resource}}` is **not** in `action_scope`: -**Field is null or missing in output:** -> "Field `{{field}}` is missing. Let's debug: -> 1. Run with `--debug` to see the raw provider response: `npx @stackone/cli test {{provider}} {{action}} --debug` -> 2. Find `{{field}}` in the raw response — what is the actual JSONPath? -> 3. Update the `expression` in `fieldConfigs` to match." +1. Check if any soft-cleanup action is available in `action_scope` for this resource — for example: `archive_{{resource}}`, `deactivate_{{resource}}`, `cancel_{{resource}}` +2. If a soft-cleanup action exists: run it and mark the artifact as `cleaned_up: true, reason: "soft-deleted via {{action}}"` +3. If no cleanup is possible at all: mark the artifact as: -Show the fix in the partial YAML and re-test. +```json +{ + "resource": "{{resource}}", + "id": "{{id}}", + "cleaned_up": false, + "reason": "no delete endpoint" +} +``` + +Always write `test_artifacts` back to the session file after each cleanup attempt — never batch this at the end. + +### Cleanup Report -**Enum value passes through unmapped:** -> "The value `{{value}}` for `{{field}}` isn't in the `enumMapper`. Add it:" -```yaml -enumMapper: - {{value}}: {{schema_value}} +After all resources have been tested, print this summary: + +``` +Test cleanup summary: +✓ {{resource}}/{{id}} — created and deleted +✓ {{resource}}/{{id}} — created and soft-deleted via archive_{{resource}} +⚠ {{resource}}/{{id}} — created, no delete endpoint; record remains in {{provider}} ``` -**Pagination not advancing:** -> "The next page isn't being fetched. Check: -> 1. What pagination style does `{{provider}}` use? (page number, cursor token, or link header) -> 2. In the raw response, where does the 'next page' value live? -> 3. Update the `cursor.responsePath` in the action to point to that field." +Use `✓` for fully cleaned up records and `⚠` for records that remain. + +--- + +## Step 5: Diagnose and Fix Failures + +If any action returns an error, diagnose and fix before moving on: -**Authentication error (401/403):** -> "Authentication failed. Check: -> 1. Is the `.env` variable name correct? -> 2. Does the `authentication` block reference it correctly? -> 3. Is the credential still valid (not expired)?" +**HTTP 401 or 403:** +- Credentials are missing or incorrect +- Re-check the `.env` file — confirm the variable name matches the `authentication` block exactly +- Confirm the API key or token has not expired -**404 / endpoint not found:** -> "The API endpoint returned 404. Verify `entrypointUrl` against the provider's API docs — the path may be wrong or require a different base URL." +**HTTP 404:** +- The endpoint path in the connector YAML may be wrong +- Check the provider's API docs for the correct path +- Confirm any path parameters (e.g. `:id`) are being substituted correctly in the `url` field -After each fix, re-test the affected action. Repeat until all actions pass. +**HTTP 422 or 400:** +- The request body format is wrong +- Check the provider API docs for required fields on this endpoint +- Review the `body` block in the connector action YAML — confirm field names and types match what the provider expects +- Run with `--debug` to see the exact request being sent: `npx @stackone/cli test {{provider}} {{action}} --debug` + +**Unexpected response shape:** +- The response structure does not match what was expected for verification +- Run with `--debug` to see the raw response: `npx @stackone/cli test {{provider}} {{action}} --debug` +- This is expected for generic connectors — the raw provider response is returned as-is, so update your verification expectations to match what the provider actually returns + +After fixing any issue, re-run the affected action before continuing. --- -## Step 5: Final summary +## Step 6: Final Summary -When all actions pass: -> "All tests passed! ✓ -> -> **Connector summary:** -> -> | Resource | Actions | Status | -> |----------|---------|--------| -> | `{{resource1}}` | {{actions1}} | ✓ | -> | `{{resource2}}` | {{actions2}} | ✓ | +When all actions have been tested and cleanup is complete: + +### Results Table + +Print a table of every action tested: + +``` +Resource | Action | Status +-----------------|----------|-------- +{{resource}} | list | ✓ passed +{{resource}} | get | ✓ passed +{{resource}} | create | ✓ passed +{{resource}} | update | ✓ passed +{{resource}} | delete | ✓ passed +``` + +### Cleanup Report + +Print the cleanup report from Step 4. + +### Next Steps + +> All tests passed. Here is what to do next: > -> **Next steps:** -> 1. Commit your connector: `git add src/configs/{{provider}}/ && git commit -m 'feat: add {{provider}} unified connector'` -> 2. If you'd like StackOne to index this connector, open a pull request to the connectors repository. -> 3. Test end-to-end with a real integration account." +> 1. Commit the connector YAML to the connectors repo +> 2. Open a pull request targeting the `main` branch of the connectors repo +> 3. Request a review from the connectors team +> 4. Once merged, test with a real integration account via the StackOne dashboard + +### Scramble Credentials + +**Always run this, even if some cleanup failed:** + +```json +scramble_credentials("{{provider}}") +``` + +This invalidates the test credentials so they cannot be reused or leaked. + +### Save Session + +Write the following to `.connector-build-session.json`: + +```json +{ + "tested": true, + "completed_at": "{{datetime}}" +} +``` + +Merge these fields into the existing session object — do not overwrite the entire file. + +--- -Save `"tested": true` and `"completed_at": "{{datetime}}"` to session. +The connector is complete. diff --git a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md index 514f3e4..96be2aa 100644 --- a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md @@ -1,114 +1,226 @@ --- name: validate-connector -description: Step 5 of building a unified connector. Runs stackone validate on the connector config, interprets any errors with clear fixes, re-runs until clean, and confirms structural correctness before live testing. +description: Step 5 of building a generic Falcon connector. Validates the generated YAML config using the StackOne CLI (or a manual checklist if CLI is unavailable), interprets and fixes common errors, then confirms the connector is ready for live testing. invoke: validate-connector --- # Validate Connector -Step 5 of the unified connector build process. +Step 5 of the generic connector build process. ## Session File Read `.connector-build-session.json`. Confirm: -> "Validating connector at `src/configs/{{provider}}/`" +> "Validating connector config for `{{provider}}` at `{{connector_path}}`." -If `connector_path` is missing: -> "No connector path in session. Run `/check-connector` first." - -Read `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the error table. +If `session_step` is not `"validate-connector"` or earlier steps are incomplete: +> "It looks like the config hasn't been built yet. Run `/build-config` first." --- ## Step 1: Run validation -**If `cli_available` is true:** +Reference `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for CLI usage and error table. + +### If `cli_available: true` + +Run: ```bash npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.yaml ``` -**If `cli_available` is false (manual review):** +Capture the full output. Proceed to Step 2 to interpret any errors. + +### If `cli_available: false` + +The CLI is not available. Run the manual checklist below against every partial file in `src/configs/{{provider}}/`. + +**Manual checklist for generic connectors:** + +Work through each action across all `{{provider}}.{{resource}}.s1.partial.yaml` files: + +1. **`actionType: custom`** — Every action must have `actionType: custom`. No other value is valid for generic connectors. + +2. **`entrypointUrl` present** — Every action must have an `entrypointUrl`. It must be a string starting with `/` or a full URL. Template expressions like `${inputs.id}` are allowed. + +3. **`entrypointHttpMethod` present** — Every action must have `entrypointHttpMethod`. Valid values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. + +4. **At least one `type: request` step** — Every action must have a `steps` array with at least one entry where `type: request`. Generic actions have exactly one step. + +5. **`list` actions have pagination inputs** — Actions named `list_*` must have at least one pagination input (`page`, `per_page`, `offset`, `limit`, or provider-specific equivalent) with `required: false`. + +6. **`get` actions reference `${inputs.id}` in URL** — Actions named `get_*` must have `${inputs.id}` in `entrypointUrl` and an `id` input with `required: true`. + +7. **`create` actions use POST** — Actions named `create_*` must have `entrypointHttpMethod: POST` and a `body` input with `type: object` and `required: true`. + +8. **`update` actions use PATCH or PUT** — Actions named `update_*` must have `entrypointHttpMethod: PATCH` or `entrypointHttpMethod: PUT`. + +9. **`delete` actions use DELETE** — Actions named `delete_*` must have `entrypointHttpMethod: DELETE`. + +10. **All `$ref` paths in main connector file exist** — Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. For every `$ref` entry under `actions`, verify the referenced partial file exists on disk at the given relative path. -Read each `.yaml` and `.partial.yaml` file in `src/configs/{{provider}}/` and check: -- All unified actions have `schemaType: unified` -- All unified actions have a `fieldConfigs` array -- All unified actions have all three steps: `request`, `map_fields` (version 2), `typecast` (version 2) -- `list` actions have a `cursor` block -- `get` actions reference `${inputs.id}` in the URL and have `id` in `inputs` -- No typos in `actionType` values (valid: `custom`, `list`, `get`, `create`, `update`, `delete`) -- All `$ref` paths in the main connector point to files that exist -- No tabs — use 2-space indentation throughout +11. **No tabs — 2-space indentation only** — Scan each YAML file for tab characters (`\t`). YAML does not allow tabs. All indentation must use spaces (2 per level). + +Report each failure with the file name, action name, and the specific check that failed. --- ## Step 2: Interpret and fix errors -For each validation error, explain what it means and show the exact fix. +For each error from the CLI output or the manual checklist, identify the cause and apply a fix. + +### Common errors and fixes + +**`Missing required field: entrypointUrl`** -**Missing `entrypointUrl`:** -> "The action is missing an endpoint URL. Add `entrypointUrl` pointing to the provider's API path:" +Cause: The action YAML block is missing the `entrypointUrl` key. + +Fix — add the field: ```yaml -entrypointUrl: "/{{resource}}" -entrypointHttpMethod: GET +- name: list_employees + actionType: custom + entrypointUrl: "/employees" # <-- add this + entrypointHttpMethod: GET ``` -**Wrong `actionType`:** -> "The actionType value is invalid. Valid values are: `custom`, `list`, `get`, `create`, `update`, `delete`" +--- -**Missing `fieldConfigs` on unified action:** -> "Unified actions require a `fieldConfigs` array for field mapping. Run `/map-fields` if you haven't mapped fields yet, or add the array:" +**`YAML parse error at line N`** or tab indentation error + +Cause: Tabs used instead of spaces, or inconsistent indentation. + +Fix — replace all tabs with 2-space indentation. Example of wrong vs correct: + +Wrong (tab-indented): ```yaml -fieldConfigs: - - targetFieldKey: id - expression: "$.id" +- name: list_employees + actionType: custom # tab here — invalid ``` -**Missing steps on unified action:** -> "Unified actions need all three pipeline steps:" +Correct (2-space indented): ```yaml -steps: - - type: request - id: fetch - - type: map_fields - version: 2 - id: map - - type: typecast - version: 2 - id: cast +- name: list_employees + actionType: custom ``` -**Missing `schemaType`:** -> "Add `schemaType: unified` to the action." +--- -**Bad `$ref` path:** -> "The partial file path in `$ref` doesn't exist. Check the filename matches exactly — naming convention is `{{provider}}.{{resource}}.s1.partial.yaml`" +**`Unknown actionType`** or invalid actionType value -After each fix, re-run validation. Repeat until it passes cleanly. +Cause: Typo or wrong value in `actionType`. + +Fix — the only valid value for generic connectors is `custom`: +```yaml +actionType: custom +``` + +--- + +**`$ref` path not found** or partial file missing + +Cause: The `$ref` in the main connector YAML points to a file that doesn't exist, or the filename doesn't match the actual file on disk. + +Fix — verify the partial file exists and the path matches exactly. Example: + +Main connector YAML: +```yaml +actions: + - $ref: "./{{provider}}.employees.s1.partial.yaml" +``` + +The file `src/configs/{{provider}}/{{provider}}.employees.s1.partial.yaml` must exist. Check for typos in the provider name, resource name, or `.s1.partial.yaml` suffix. + +--- + +**`get` action missing `id` input or `${inputs.id}` in URL** + +Cause: The `get` action was written without an `id` input or with a hardcoded path. + +Fix: +```yaml +- name: get_employee + actionType: custom + entrypointUrl: "/employees/${inputs.id}" # must reference ${inputs.id} + entrypointHttpMethod: GET + inputs: + id: + type: string + required: true + steps: + - type: request + id: fetch_employee +``` + +--- + +**`create` action missing `body` input or wrong HTTP method** + +Cause: POST action is missing the request body input, or uses GET instead of POST. + +Fix: +```yaml +- name: create_employee + actionType: custom + entrypointUrl: "/employees" + entrypointHttpMethod: POST # must be POST + inputs: + body: + type: object + required: true # must be required + steps: + - type: request + id: create_employee +``` + +--- + +**`list` action missing pagination inputs** + +Cause: No pagination inputs defined, which means the caller cannot page through results. + +Fix — add pagination inputs with `required: false`: +```yaml +inputs: + page: + type: number + required: false + per_page: + type: number + required: false +``` + +Use the provider's actual parameter names if they differ (e.g., `offset`/`limit`, `pageToken`, `cursor`). + +--- + +After fixing each error, re-run the CLI validate command (if available) or re-run the manual checklist to confirm all issues are resolved. --- ## Step 3: Confirm clean validation -When validation passes (or manual review finds no issues): -> "Validation passed — your connector config is structurally correct. ✓ -> -> **Summary:** -> - Provider: `{{provider}}` -> - Resources: {{resources}} -> - Actions: {{action_scope}} -> - Config path: `src/configs/{{provider}}/`" +Once validation passes with no errors: -Save `"validated": true` to session. +> "Validation passed. ✓ +> +> **`{{provider}}` connector summary:** +> - Config: `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` +> - Partial files: {{list each partial file}} +> - Actions validated: {{N}} ({{list action names}}) +> - All actions use `actionType: custom` +> - All `$ref` paths resolved +> - YAML structure is valid" --- ## Handoff -> "Ready for live testing. +Update the session file: +- Set `"validated": true` +- Set `"session_step": "test-connector"` + +> "The connector config is valid and ready for live testing. > -> Next step: test your actions against the real `{{provider}}` API. -> Run `/test-connector` to continue. +> Next: run `/test-connector` to make real API calls against `{{provider}}`. > -> Before running tests, make sure your `.env` file has the right credentials." - -Update `session_step` to `"test-connector"` in the session file. +> Before you do, make sure your `.env` file has the credentials for `{{provider}}`. The CLI reads them automatically — check `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the expected variable names and format." diff --git a/.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json b/.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json new file mode 100644 index 0000000..75a8ca2 --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "stackone-unified-builder", + "version": "1.0.0", + "description": "Interactive step-by-step wizard for building unified Falcon connectors. Walks external builders through schema selection, connector scaffolding, action scoping, field mapping, validation, and testing.", + "author": { + "name": "StackOne", + "email": "engineering@stackone.com" + }, + "license": "MIT", + "keywords": ["connector", "falcon", "unified", "schema", "stackone", "builder"] +} diff --git a/.claude/plugins/stackone-unified-builder/README.md b/.claude/plugins/stackone-unified-builder/README.md new file mode 100644 index 0000000..680d08f --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/README.md @@ -0,0 +1,68 @@ +# stackone-connector-builder + +Interactive step-by-step wizard for building unified Falcon connectors. Guides external builders through every phase — from choosing an output schema to live API testing. + +## Installation + +```bash +/plugin install stackone-connector-builder@stackone-claude-marketplace +``` + +## Usage + +### Full wizard (recommended) + +```bash +/build-connector +``` + +Walks through all 6 steps in sequence. Progress is saved to `.connector-build-session.json` so you can pause and resume at any time. + +### Individual steps + +Each step can also be run independently. It reads the session file and picks up from the right context: + +| Command | Step | What it does | +|---------|------|-------------| +| `/choose-schema` | 1 | Pick a StackOne built-in schema, point to your own, or define one interactively | +| `/check-connector` | 2 | Check if a base connector exists, pull it or scaffold a new one | +| `/scope-actions` | 3 | Choose which resources and CRUD operations to expose | +| `/map-fields` | 4 | Map provider API fields to your schema, writes YAML partials | +| `/validate-connector` | 5 | Validate the YAML config structure | +| `/test-connector` | 6 | Test live against the provider API and verify field output | + +### Resume a session + +```bash +/build-connector +``` + +If a `.connector-build-session.json` exists, the wizard will offer to resume from where you left off. + +## What it builds + +A set of YAML configuration files in `src/configs//` that expose provider API data through StackOne's unified API. Supports both StackOne built-in schemas (HRIS, ATS, CRM, etc.) and custom schemas you define. + +## Schema options + +| Option | When to use | +|--------|-------------| +| **StackOne built-in** | Your data fits HRIS, ATS, CRM, LMS, IAM, Ticketing, Documents, or Marketing | +| **Existing schema file** | You already have a JSON/YAML schema in your project | +| **Define inline** | You want to define fields interactively during the wizard | + +## Requirements + +- `connectors-template` project structure (`src/configs/` directory) +- Node.js — for the StackOne CLI (optional but recommended) +- Provider API credentials in `.env` + +## How sessions work + +Each step reads and writes `.connector-build-session.json` at the project root. This file stores: +- Provider name and schema choice +- CLI availability and connector setup status +- Action scope and known limitations +- Validation and test results + +You can safely delete this file to start fresh. diff --git a/.claude/plugins/stackone-unified-builder/references/actions-and-steps.md b/.claude/plugins/stackone-unified-builder/references/actions-and-steps.md new file mode 100644 index 0000000..4f58396 --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/references/actions-and-steps.md @@ -0,0 +1,302 @@ +# Actions and Steps Reference + +Technical reference for writing action YAML files — covering action structure, inputs, step functions, expressions, and result mapping. + +## Action Structure + +### Required Fields +- `actionId`: Unique identifier +- `categories`: List of categories for StackOne UI +- `actionType`: `custom` (default for non-unified) or `list|get|create|update|delete` (unified only) +- `label`: Human-readable name +- `description`: Short description (shown in UI) +- `steps`: List of step functions +- `result`: Final output + +### Optional Fields +- `details`: Longer description (tool description, rate limits, required permissions) +- `resources`: Action-specific documentation URLs +- `examples`: Input/output demonstration pairs +- `inputs`: Request parameters +- `requiredScopes`: Space-separated OAuth scopes (must be defined in `scopeDefinitions`). Use most restrictive scope. +- `entrypointUrl` / `entrypointHttpMethod`: **Unified actions ONLY** (DO NOT USE for non-unified) + +## Inputs + +For non-unified actions: inputs must match EXACTLY the provider's request parameters. DO NOT create inputs that don't exist in the provider API. Ignore deprecated fields. + +### Supported Types +`string`, `number`, `boolean`, `datetime_string`, `object`, `enum` + +**Never use `type: array`** — always use `array: true` with element type. + +### Input Examples + +**Basic string/number:** +```yaml +inputs: + - name: userId + description: User identifier + type: string + in: path + required: true +``` + +**Object:** +```yaml +inputs: + - name: filter + description: Filter object + type: object + in: body + required: true + properties: + - name: name + description: Filter by name + type: string + required: false +``` + +**Array:** +```yaml +inputs: + - name: userIds + description: Array of user IDs + type: string + array: true + in: body + required: true +``` + +**Enum:** +```yaml +inputs: + - name: status + description: Employment status + type: enum + required: true + in: query + oneOf: + values: + - active + - inactive + - terminated +``` + +### Input locations (`in` field) +- `query` — URL query parameter +- `body` — request body +- `path` — URL path parameter (referenced via `${inputs.fieldName}` in URL) +- `headers` — request header + +## Expression Formats + +### 1. JSONPath (`$.path.to.field`) — PREFERRED +For direct references without string construction: +- Credentials: `$.credentials.apiKey` +- Inputs: `$.inputs.userId` +- Step output: `$.steps.fetch_users.output.data` + +### 2. String Interpolation (`${...}`) +For embedding dynamic values in strings: +- URLs: `/users/${inputs.userId}/posts/${inputs.postId}` +- Domains: `https://${credentials.domain}.api.com` + +### 3. JEXL Expressions (`'{{...}}'`) +For conditional logic, transformations. **Wrap in single quotes.** +- Conditionals: `'{{present(inputs.includeInactive)}}'` +- Ternary: `'{{$.status == "active" ? "enabled" : "disabled"}}'` +- String manipulation: `'{{inputs.name.toUpperCase()}}'` + +**IMPORTANT:** +- For `value` fields: use JSONPath `$.inputs.fieldName` +- For `condition` fields: use JEXL `'{{present(inputs.fieldName)}}'` +- **Never use JEXL for `value` fields** + +## Step Functions + +Every step MUST have a `description` field. + +### `request` — Standard HTTP request + +```yaml +steps: + - stepId: fetch_users + description: List users from the API + stepFunction: + functionName: request + parameters: + url: '/users' + method: get + args: + - name: showInactive + value: $.inputs.showInactive + in: query + condition: '{{present(inputs.showInactive)}}' +``` + +Always use `args` for parameters (never direct `body` field). + +**Custom headers for `authorization.type: none`:** +```yaml +args: + - name: X-API-Key + value: $.credentials.customKey + in: headers +``` + +**Raw array bodies** — when API requires `[...]` instead of `{...}`: +```yaml +args: + - name: events + value: $.inputs.events + in: body + spread: true +``` + +**Custom error mapping:** +```yaml +customErrors: + - receivedStatus: 404 + targetStatus: 400 + message: 'Custom error message' +``` + +### `paginated_request` — Cursor-based pagination + +Only use if provider supports cursor/offset pagination. Otherwise use `request`. + +```yaml +steps: + - stepId: list_records + description: Fetch records with pagination + stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query +``` + +### `soap_request` — SOAP API calls + +```yaml +steps: + - stepId: get_employee + description: Fetch employee via SOAP + stepFunction: + functionName: soap_request + parameters: + url: /EmployeeService + method: post + soapOperation: GetEmployee + soapAction: http://example.com/soap/GetEmployee + useSoapContext: false + namespaces: + - namespaceIdentifier: emp + namespace: http://example.com/employees + args: + - name: EmployeeId + value: ${inputs.employee_id} + in: body +``` + +Key parameters: +- `soapOperation`: SOAP operation name +- `soapAction`: SOAP action URI +- `namespaces`: XML namespace definitions +- `useSoapContext`: Set to `false` when provider expects payload as-is +- Prefix XML attributes with `@_` (e.g., `@_xsi:type`) + +### Other Step Functions +- `group_data`: Groups data from multiple steps +- `map_fields`: Maps using `fieldConfigs` (unified actions only) +- `typecast`: Applies types from `fieldConfigs` (unified actions only) + +## Field Configs (Unified Actions Only) + +NOT required for non-unified connectors. Maps provider response to StackOne unified schema: + +```yaml +fieldConfigs: + - targetFieldKey: id + expression: $.accountId + type: string + - targetFieldKey: type + expression: $.accountType + type: enum + enumMapper: + matcher: + - matchExpression: '{{$.accountType == "atlassian"}}' + value: agent + - matchExpression: '{{$.accountType == "app"}}' + value: bot + - targetFieldKey: active + expression: $.active + type: boolean +``` + +## Result Mapping + +```yaml +# Read response +result: + data: $.steps.fetch_users.output.data + +# Write response +result: + message: Resource updated successfully + data: + id: $.inputs.id +``` + +## GraphQL Patterns + +Reference: `linear` connector + +**Input structure — always use nested `variables` object:** +```yaml +inputs: + - name: variables + description: Variables for the query + type: object + in: body + properties: + - name: first + description: Number of items + type: number + required: false + - name: filter + description: Filter object + type: object + required: false +``` + +**Request:** +```yaml +args: + - name: Content-Type + value: application/json + in: headers + - name: query + value: "query($first: Int) { resources(first: $first) { nodes { id name } } }" + in: body + - name: variables + in: body + condition: "{{present(inputs.variables)}}" + value: + { first: $.inputs.variables.first } +``` + +**IMPORTANT for nested objects:** When querying nested objects, ONLY return the `id` field if a separate action exists to fetch the full object. Don't return full nested objects. + +**Query patterns:** +- List: `query($first: Int, $after: String) { resources(first: $first, after: $after) { nodes { id name } pageInfo { hasNextPage endCursor } } }` +- Get: `query($id: String!) { resource(id: $id) { id name description } }` +- Create: `mutation($input: CreateInput!) { create(input: $input) { success resource { id } } }` +- Update: `mutation($id: String!, $input: UpdateInput!) { update(id: $id, input: $input) { success } }` diff --git a/.claude/plugins/stackone-unified-builder/references/auth-patterns.md b/.claude/plugins/stackone-unified-builder/references/auth-patterns.md new file mode 100644 index 0000000..fe6c04b --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/references/auth-patterns.md @@ -0,0 +1,209 @@ +# Authentication Patterns Reference + +Detailed YAML patterns for every authentication type supported by the Falcon framework. +Used by the `configure-auth` sub-skill. + +## Decision Guide + +- Token exchange via endpoint call? → `type: oauth2` +- No token exchange? → `type: custom` +- Custom headers (not standard Authorization)? → `type: custom` with `authorization.type: none`, inject headers in every action's step args + +## Key Concepts + +- `setupFields` (T1-facing): Multi-tenant/app-level credentials (OAuth Client ID/Secret, scopes) +- `configFields` (T2-facing): End-user specific credentials (API token, username, sub-domain) +- `testActions`: Verify credentials on connection — use action requiring minimal input (e.g., `list_users`) +- `environments`: Define available environments (production, sandbox) +- `support`: Link + description for connection guide + +## Authorization Types (under `custom`) + +| Type | Requires | Use case | +|------|----------|----------| +| `basic` | `username`, `password` | Username + password base64-encoded in Authorization header | +| `bearer` | `token` | Static bearer token in Authorization header | +| `apiKey` | `key`, `value`, `in` (header or query) | API key in a named header or query parameter | +| `none` | — | Inject credentials manually in every action's step args | + +## Referencing Credentials + +- **JSONPath** (for values): `$.credentials.apiKey` +- **String Interpolation** (for URLs): `https://${credentials.subdomain}.api.com` + +--- + +## Custom Auth — Basic Auth + +```yaml +authentication: + - custom: + type: custom + label: API Key + support: + link: https://hub.stackone.com/connection-guides/provider + description: Admin privileges required + authorization: + type: basic + username: $.credentials.email + password: $.credentials.accessToken + configFields: + - key: email + label: Email Address + type: string + required: true + - key: accessToken + label: API Token + type: password + required: true + secret: true + placeholder: ATATT3xFfGF0aQNaJZ... + description: Generate via Account Settings > Security > API tokens + tooltip: Save securely, won't be shown again + environments: + - key: production + name: Production + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — Bearer Token + +```yaml +authentication: + - custom: + type: custom + label: API Key + authorization: + type: bearer + token: $.credentials.apiKey + configFields: + - key: apiKey + label: API Key + type: password + required: true + secret: true + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — API Key Header + +```yaml +authentication: + - custom: + type: custom + label: API Key + authorization: + type: apiKey + key: X-API-Key + value: $.credentials.apiKey + in: header + configFields: + - key: apiKey + label: API Key + type: password + required: true + secret: true + testActions: + - action: list_users + required: true +``` + +--- + +## Custom Auth — Custom Headers (type: none) + +When the API needs non-standard headers, set `authorization.type: none` and pass credentials in EVERY action's step args: + +```yaml +# In connector YAML +authentication: + - custom: + type: custom + label: API Key + authorization: + type: none + configFields: + - key: customKey + label: Custom Header API Key + type: password + required: true + testActions: + - action: list_users + required: true + +# In EVERY partial action step: +stepFunction: + functionName: request + parameters: + url: '/users' + method: get + args: + - name: X-API-Key + value: $.credentials.customKey + in: headers +``` + +--- + +## OAuth 2.0 — Authorization Code + +```yaml +authentication: + - oauth2: + type: oauth2 + label: OAuth 2.0 + grantType: authorization_code + authorizationUrl: https://api.provider.com/oauth/authorize + tokenUrl: https://api.provider.com/oauth/token + setupFields: + - key: clientId + label: Client ID + type: string + required: true + - key: clientSecret + label: Client Secret + type: password + required: true + secret: true + scopes: + - resource.read + - resource.write + testActions: + - action: get_current_user + required: true +``` + +If the refresh token request does not require authorization headers, add: + +```yaml +refreshAuthentication: + authorization: + type: none +``` + +--- + +## Security + +- ALWAYS run `scramble_credentials()` with `securityLevel: PRODUCTION` before storing/committing +- Never use `DEBUG` preset in production +- Supply `customPatterns` for provider-specific credential formats + +--- + +## Common Auth Errors + +| Error | Cause | Fix | +|-------|-------|-----| +| `401 Unauthorized` | Wrong credentials or expired token | Check `.env` values, re-authenticate | +| `403 Forbidden` | Valid credentials, insufficient permissions | Add required scopes or API permissions | +| Literal `$.credentials.apiKey` in request | Interpolation not working | Ensure configFields key matches the reference | +| `base64 is not defined` | Basic auth syntax error | Use the `authorization.type: basic` pattern above | diff --git a/.claude/plugins/stackone-unified-builder/references/cli-commands.md b/.claude/plugins/stackone-unified-builder/references/cli-commands.md new file mode 100644 index 0000000..72ac83b --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/references/cli-commands.md @@ -0,0 +1,74 @@ +# StackOne CLI Reference + +Used by the `check-connector` and `validate-connector` sub-skills. + +## Installation Check + +```bash +npx @stackone/cli --version +``` + +If this returns a version number, the CLI is available. If it errors, proceed with manual scaffolding. + +## Key Commands + +### Validate a connector config +```bash +npx @stackone/cli validate src/configs//.connector.s1.yaml +``` +Checks YAML structure, required fields, and action definitions. Returns validation errors with line numbers. + +### List available connectors +```bash +npx @stackone/cli list +``` +Returns a list of provider names StackOne has indexed. Use to check if a connector already exists before scaffolding from scratch. + +### Pull an existing connector +```bash +npx @stackone/cli pull +``` +Downloads the connector config to `src/configs//`. Creates the folder and YAML files if they don't exist. Use as a starting point. + +### Test an action +```bash +npx @stackone/cli test +``` +Runs a live API call against the action using credentials in `.env`. Returns the raw provider response followed by the mapped unified response. + +Add `--debug` to see the raw provider response before field mapping is applied: +```bash +npx @stackone/cli test --debug +``` + +### Scaffold a new connector +```bash +npx @stackone/cli scaffold +``` +Creates `src/configs//` with a starter `.connector.s1.yaml`. Does NOT pull from StackOne's index — creates a blank template. + +## Environment Setup + +The CLI reads credentials from `.env` in the project root. Variable names follow `_` in uppercase: + +```bash +# .env examples +BAMBOOHR_API_KEY=your_key_here +SALESFORCE_CLIENT_ID=your_client_id +SALESFORCE_CLIENT_SECRET=your_client_secret +WORKDAY_BASE_URL=https://api.workday.com +``` + +## Common CLI Errors + +| Error | Cause | Fix | +|-------|-------|-----| +| `YAML parse error at line N` | Invalid YAML syntax | Check indentation — use 2 spaces, not tabs | +| `Missing required field: entrypointUrl` | Action missing URL | Add `entrypointUrl` to the action | +| `Unknown actionType: custom2` | Typo in actionType | Valid: `custom`, `list`, `get`, `create`, `update`, `delete` | +| `schemaType required for unified action` | Missing schemaType | Add `schemaType: unified` to the action | +| `fieldConfigs required when schemaType is unified` | Missing field mapping | Add `fieldConfigs` array to the action | +| `Missing step: map_fields` | Unified action missing pipeline step | Add all three steps: `request`, `map_fields`, `typecast` | +| `401 Unauthorized` | Bad credentials | Check `.env` and the `authentication` block in connector YAML | +| `404 Not Found` | Wrong endpoint URL | Verify `entrypointUrl` against provider API docs | +| `Cannot find module '@stackone/cli'` | CLI not installed | Run `npm install -g @stackone/cli` or use `npx` | diff --git a/.claude/plugins/stackone-unified-builder/references/connector-patterns.md b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md new file mode 100644 index 0000000..e484082 --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md @@ -0,0 +1,538 @@ +# Connector Patterns Reference + +Official Falcon YAML patterns for connector configurations. +Used by `map-fields` and `validate-connector` sub-skills. + +## File Structure + +``` +src/configs// +├── .connector.s1.yaml # Main connector (auth, base config) +└── ..s1.partial.yaml # Actions for each resource +``` + +## Main Connector File Template + +```yaml +StackOne: 1.0.0 +info: + title: Provider Name + key: provider_name + version: 1.0.0 + assets: + icon: https://stackone-logos.com/api/provider_name/filled/png + description: Brief description + details: | + Extended information about this connector + +baseUrl: https://api.provider.com +releaseStage: preview + +rateLimit: + mainRatelimit: 10 + +resources: https://api.provider.com/docs + +scopeDefinitions: + employees.write: + description: Allow writing and modifying employees + includes: employees.read + employees.read: + description: Allow reading employees + +authentication: + # See auth-patterns.md for full authentication options + +actions: + $ref: provider_name.employees + $ref: provider_name.departments +``` + +## Partial File Pattern ($ref) + +The main connector references partial files. Each partial starts directly with `- actionId:` — there is **NO `actions:` wrapper** in partial files. + +**In the main connector:** +```yaml +actions: + $ref: provider_name.employees + $ref: provider_name.departments +``` + +**In `provider_name.employees.s1.partial.yaml`:** +```yaml +- actionId: list_employees + actionType: custom + # ... + +- actionId: get_employee + actionType: custom + # ... +``` + +--- + +## Non-Unified Actions + +Non-unified actions return the provider's raw response without StackOne schema mapping. +They use `stepFunction`/`functionName`/`parameters`, `stepId`, and `actionId`. +They do NOT use `entrypointUrl`, `entrypointHttpMethod`, or `response:` blocks. + +### Non-Unified Action — List (with pagination) + +```yaml +- actionId: list_employees + categories: [employees] + actionType: custom + label: List Employees + description: Retrieve a list of employees + details: | + Fetches all employees with pagination support. Rate limit: 100/min. + resources: https://api.provider.com/docs/employees + examples: + - input: { page: 1 } + output: { data: [{ id: "1", name: "John Doe" }] } + requiredScopes: employees.read + inputs: + - name: page + description: Page number to retrieve + type: number + in: query + required: false + steps: + - stepId: list_employees + description: Fetch employees with pagination + stepFunction: + functionName: paginated_request + parameters: + url: "/employees" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query + result: + data: $.steps.list_employees.output.data +``` + +### Non-Unified Action — Get (single record) + +```yaml +- actionId: get_employee + categories: [employees] + actionType: custom + label: Get Employee + description: Retrieve a single employee by ID + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + steps: + - stepId: get_employee + description: Fetch a single employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: get + result: + data: $.steps.get_employee.output.data +``` + +### Non-Unified Action — Create (POST with body) + +```yaml +- actionId: create_employee + categories: [employees] + actionType: custom + label: Create Employee + description: Create a new employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write + inputs: + - name: first_name + description: Employee first name + type: string + in: body + required: true + - name: last_name + description: Employee last name + type: string + in: body + required: true + - name: email + description: Employee work email + type: string + in: body + required: false + steps: + - stepId: create_employee + description: Create a new employee + stepFunction: + functionName: request + parameters: + url: "/employees" + method: post + args: + - name: firstName + value: $.inputs.first_name + in: body + - name: lastName + value: $.inputs.last_name + in: body + - name: email + value: $.inputs.email + in: body + condition: "{{present(inputs.email)}}" + result: + data: $.steps.create_employee.output.data +``` + +### Non-Unified Action — Update (PATCH/PUT with ID + body) + +```yaml +- actionId: update_employee + categories: [employees] + actionType: custom + label: Update Employee + description: Update an existing employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + - name: first_name + description: Updated first name + type: string + in: body + required: false + - name: last_name + description: Updated last name + type: string + in: body + required: false + steps: + - stepId: update_employee + description: Update an employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: patch + args: + - name: firstName + value: $.inputs.first_name + in: body + condition: "{{present(inputs.first_name)}}" + - name: lastName + value: $.inputs.last_name + in: body + condition: "{{present(inputs.last_name)}}" + result: + data: $.steps.update_employee.output.data +``` + +### Non-Unified Action — Delete + +```yaml +- actionId: delete_employee + categories: [employees] + actionType: custom + label: Delete Employee + description: Delete an employee record + resources: https://api.provider.com/docs/employees + requiredScopes: employees.write + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + steps: + - stepId: delete_employee + description: Delete an employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: delete + result: + data: $.steps.delete_employee.output +``` + +--- + +## Unified Actions + +Unified actions map provider data to a StackOne unified schema. +They use `schemaType: unified`, `schema`, `cursor`, `fieldConfigs`, and three steps: `request`, `map_fields` (v2), `typecast` (v2). +Unified actions use `entrypointUrl` and `entrypointHttpMethod`. +Unified actions use a `response:` block inside the request step. + +### Unified Action — List (paginated) + +```yaml +- actionId: unified_list_employees + categories: [employees] + actionType: list + schemaType: unified + schema: hris/employees + label: List Employees (Unified) + description: Retrieve a unified list of employees + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read + entrypointUrl: "/employees" + entrypointHttpMethod: GET + cursor: + type: page + requestParam: page + responsePath: $.meta.next_page + fieldConfigs: + - targetFieldKey: id + expression: "$.id" + - targetFieldKey: first_name + expression: "$.firstName" + - targetFieldKey: last_name + expression: "$.lastName" + - targetFieldKey: work_email + expression: "$.workEmail" + - targetFieldKey: employment_status + expression: "$.status" + enumMapper: + active: active + inactive: inactive + terminated: inactive + steps: + - stepId: fetch_employees + description: Fetch employees from provider API + stepFunction: + functionName: request + parameters: + url: "/employees" + method: get + response: + dataKey: data + nextKey: meta.next_page + - stepId: map_employee_fields + description: Map provider fields to unified schema + stepFunction: + functionName: map_fields + version: 2 + - stepId: cast_types + description: Cast fields to correct types + stepFunction: + functionName: typecast + version: 2 +``` + +### Unified Action — Get (single record) + +```yaml +- actionId: unified_get_employee + categories: [employees] + actionType: get + schemaType: unified + schema: hris/employees + label: Get Employee (Unified) + description: Retrieve a single unified employee by ID + resources: https://api.provider.com/docs/employees + requiredScopes: employees.read + entrypointUrl: "/employees/${inputs.id}" + entrypointHttpMethod: GET + inputs: + - name: id + description: The employee ID + type: string + in: path + required: true + fieldConfigs: + - targetFieldKey: id + expression: "$.id" + - targetFieldKey: first_name + expression: "$.firstName" + - targetFieldKey: last_name + expression: "$.lastName" + steps: + - stepId: fetch_employee + description: Fetch a single employee + stepFunction: + functionName: request + parameters: + url: "/employees/${inputs.id}" + method: get + response: + dataKey: data + - stepId: map_fields + description: Map provider fields to unified schema + stepFunction: + functionName: map_fields + version: 2 + - stepId: typecast + description: Cast fields to correct types + stepFunction: + functionName: typecast + version: 2 +``` + +--- + +## Pagination Patterns + +Used in `paginated_request` for non-unified actions, or `cursor` block for unified actions. + +### Page number (most common) +```yaml +cursor: + type: page + requestParam: page + responsePath: $.pagination.next_page +``` + +### Cursor / token +```yaml +cursor: + type: cursor + requestParam: cursor + responsePath: $.meta.next_cursor +``` + +### Offset +```yaml +cursor: + type: offset + requestParam: offset + pageSize: 100 +``` + +### Link header (RFC 5988) +```yaml +cursor: + type: link_header +``` + +### paginated_request iterator patterns (non-unified) + +**Cursor-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: results + nextKey: nextCursor + iterator: + key: cursor + in: query +``` + +**Page-number-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: data + nextKey: meta.next_page + iterator: + key: page + in: query +``` + +**Offset-based:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: items + totalKey: total + iterator: + key: offset + in: query + step: 100 +``` + +**Link header:** +```yaml +stepFunction: + functionName: paginated_request + parameters: + url: "/records" + method: get + response: + dataKey: data + paginationType: link_header +``` + +--- + +## Result Mapping + +### Read actions (list/get) +```yaml +result: + data: $.steps..output.data +``` + +### Write actions (create/update) +```yaml +result: + data: $.steps..output.data +``` + +### Delete actions +```yaml +result: + data: $.steps..output +``` + +--- + +## Key Distinctions Summary + +| Feature | Non-Unified | Unified | +|---|---|---| +| `actionType` | `custom` | `list`, `get`, `create`, etc. | +| `schemaType` | not present | `unified` | +| `schema` | not present | e.g. `hris/employees` | +| `entrypointUrl` | **NOT used** | required | +| `entrypointHttpMethod` | **NOT used** | required | +| `response:` in request step | **NOT used** | required | +| `cursor` block | not present | present for list actions | +| `fieldConfigs` | not present | required | +| Steps | `request` only | `request` + `map_fields` v2 + `typecast` v2 | +| Step structure | `stepId` + `stepFunction` | `stepId` + `stepFunction` | + +**Important v2.2.0 breaking change:** Use `resources` (NOT `context`) for documentation links at both the connector and action level. + +--- + +## Auth Credential Variable Naming + +| Auth Type | Variable Pattern | Example | +|-----------|----------------|---------| +| API Key | `${PROVIDER_API_KEY}` | `${BAMBOOHR_API_KEY}` | +| OAuth Client ID | `${PROVIDER_CLIENT_ID}` | `${SALESFORCE_CLIENT_ID}` | +| OAuth Secret | `${PROVIDER_CLIENT_SECRET}` | `${SALESFORCE_CLIENT_SECRET}` | +| Username | `${PROVIDER_USERNAME}` | `${WORKDAY_USERNAME}` | +| Password | `${PROVIDER_PASSWORD}` | `${WORKDAY_PASSWORD}` | +| Base URL | `${PROVIDER_BASE_URL}` | `${WORKDAY_BASE_URL}` | +| Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | +| Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | + +Reference inside the connector using `${args..value}` after defining in the authentication `args` block. diff --git a/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md new file mode 100644 index 0000000..3053366 --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md @@ -0,0 +1,314 @@ +````markdown +# Field Mapping Guide + +Detailed reference for writing `fieldConfigs` in unified connectors. +Used by the `map-fields` sub-skill. + +## The Golden Rule + +`targetFieldKey` is ALWAYS your schema field name — NEVER the provider's field name. + +```yaml +# CORRECT — targetFieldKey uses your schema name +- targetFieldKey: first_name + expression: "$.firstName" + +# WRONG — targetFieldKey uses the provider's name +- targetFieldKey: firstName + expression: "$.firstName" +``` + +--- + +## Expression Types + +### Direct JSONPath +```yaml +- targetFieldKey: id + expression: "$.id" + +- targetFieldKey: first_name + expression: "$.firstName" +``` + +### Nested path +```yaml +- targetFieldKey: department_name + expression: "$.department.name" + +- targetFieldKey: work_email + expression: "$.contact.emails[0].value" +``` + +### JEXL expression (computed or conditional) + +Wrap JEXL in single-quotes with double-curly-brace syntax: + +```yaml +# Combine two fields into one +- targetFieldKey: display_name + expression: "'{{$.firstName}} {{$.lastName}}'" + +# Fallback — use second value if first is null/undefined +- targetFieldKey: email + expression: "'{{$.workEmail || $.personalEmail}}'" + +# Conditional +- targetFieldKey: is_active + expression: "'{{$.status === \"active\" ? true : false}}'" +``` + +### Array of primitive values +```yaml +- targetFieldKey: tags + expression: "$.tags" + array: true +``` + +--- + +## Enum Mapping + +Translate provider-specific values to your schema's enum values: + +```yaml +- targetFieldKey: employment_status + expression: "$.status" + enumMapper: + # provider_value: schema_value + active: active + inactive: inactive + terminated: inactive + on_leave: leave + pending_hire: pending +``` + +**Important:** Keys are case-sensitive and must match the provider's exact values. + +For complex logic, use `matchExpression` (JEXL): +```yaml +- targetFieldKey: gender + expression: "$.gender" + enumMapper: + matchExpression: "'{{$.gender === \"M\" ? \"male\" : $.gender === \"F\" ? \"female\" : \"other\"}}'" +``` + +--- + +## Nested Object Mapping + +> ⚠️ **`objectMapping` is NOT supported** in the `map_fields` step's `parameters.fields`. The validator will reject it. Use inline block scalar expressions instead. + +### Provider returns a nested object directly + +When the provider's response already contains the nested object (e.g., `$.location`), pass it through: + +```yaml +- targetFieldKey: work_location + expression: "$.location" + type: object +``` + +### Provider returns flat fields that must be composed into an object + +Use a YAML block scalar (`|`) with a JSON object literal. Fields are referenced with JSONPath syntax: + +```yaml +- targetFieldKey: home_location + type: object + expression: | + { + "street_1": $.address1, + "street_2": $.address2, + "city": $.city, + "zip_code": $.zipCode, + "country": $.country + } +``` + +This works in both single-step and multi-step (`group_data`) contexts. In a `group_data` context, prefix field paths with the step ID: `$.fetch_employee.address1`. + +--- + +## Array of Objects Mapping + +> ⚠️ **`objectMapping` with `array: true` is NOT supported.** Use the patterns below instead. + +### Provider returns an array directly (pass-through) + +When the provider returns the array in the right shape, pass it through: + +```yaml +- targetFieldKey: bank_details + expression: "$.bankAccounts" + type: object +``` + +In a `group_data` context, reference the step's array field: + +```yaml +- targetFieldKey: bank_details + expression: $.fetch_bank_details.data + type: object +``` + +### Construct a synthetic array from scalar fields + +When the provider stores data as flat fields but the schema expects an array (e.g., a single SSN that must become `national_identity_numbers`), use a block scalar to build the array literal: + +```yaml +- targetFieldKey: national_identity_numbers + type: object + expression: | + [{ + "value": $.fetch_employee.ssn, + "type": "ssn" + }] +``` + +### Construct a synthetic single-record array from multiple fields + +When the schema expects an array of objects but the provider surfaces that data as flat fields on the resource (e.g., employment status and dates on the employee record): + +```yaml +- targetFieldKey: employments + type: object + expression: | + [{ + "active": '{{$.fetch_employee.employmentHistoryStatus == "Active" ? true : false}}', + "effective_date": $.fetch_employee.hireDate, + "end_date": $.fetch_employee.terminationDate + }] +``` + +--- + +## Multi-Step Fetching with `group_data` + +When a resource needs data from multiple endpoints (e.g., an employee + their bank accounts + their job history), use the `group_data` step to combine results before mapping. + +### Step structure + +```yaml +steps: + - stepId: fetch_employee + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id} + method: get + + - stepId: fetch_bank_details + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/tables/directDeposit + method: get + customErrors: + - receivedStatus: 404 + targetStatus: 200 + message: No bank details found + + - stepId: group_employee_data + stepFunction: + functionName: group_data + parameters: + stepsDataToGroup: + - fetch_employee + - fetch_bank_details + isSingleRecord: true + + - stepId: map_employee_data + stepFunction: + functionName: map_fields + version: '2' + parameters: + dataSource: $.steps.group_employee_data.output.data + fields: + - targetFieldKey: id + expression: $.fetch_employee.id # prefix = stepId + - targetFieldKey: bank_details + expression: $.fetch_bank_details.data # array from second step + type: object +``` + +**Key rules:** +- `stepsDataToGroup` lists the `stepId` values to merge +- `isSingleRecord: true` when combining data for a single resource (get actions) +- After grouping, each field is accessed as `$.{stepId}.{field}` in `expression` +- The `dataSource` for `map_fields` is always `$.steps.{groupStepId}.output.data` +- Always add `customErrors` on optional sub-resource fetches to handle 404s gracefully + +--- + +## Common Provider-to-Schema Field Patterns + +| Provider Pattern | Schema Field | Expression | Notes | +|----------------|-------------|-----------|-------| +| `firstName` | `first_name` | `$.firstName` | camelCase → snake_case | +| `first_name` | `first_name` | `$.first_name` | already snake_case | +| `properties.email` | `work_email` | `$.properties.email` | HubSpot-style nested | +| `emails[0]` | `work_email` | `$.emails[0]` | First item of array | +| `contact.phone` | `phone` | `$.contact.phone` | Nested path | +| Unix timestamp | `created_at` | `$.created_at` | typecast handles conversion | +| ISO date string | `hire_date` | `$.hireDate` | typecast handles conversion | +| Flat address fields → object | `home_location` | block scalar `{...}` | See nested object section | +| Single value → array | `national_identity_numbers` | block scalar `[{...}]` | See array section | +| Sub-resource array | `bank_details` | `$.fetch_step.data` via group_data | See multi-step section | + +--- + +## Required Step Functions for Unified Actions + +All unified actions MUST include all three steps in this exact order: + +```yaml +steps: + - stepId: fetch_data + stepFunction: + functionName: request # 1. Fetch data from provider API + + - stepId: map_fields + stepFunction: + functionName: map_fields # 2. Apply field transformations + version: '2' + + - stepId: typecast + stepFunction: + functionName: typecast # 3. Convert types (dates, numbers, etc.) + version: '2' +``` + +Multi-step actions add `group_data` between the last `request` step and `map_fields`. + +Non-unified (`actionType: custom`) actions only need the `request` step. + +--- + +## Debugging Field Mapping Issues + +### See the raw provider response +```bash +npx @stackone/cli test --debug +``` +This shows exactly what the provider returns before any mapping. Use it to find the correct JSONPath. + +### Field is null in output +1. Check the JSONPath against the raw response — is the field actually there? +2. Is the field nested? Try `$.nested.field` or `$.items[0].field` +3. In a `group_data` context — are you using `$.stepId.field` not `$.field`? +4. Does the provider only return this field sometimes? Handle with JEXL fallback. + +### Enum value passes through unmapped +- Check that the `enumMapper` key matches the provider's exact value (case-sensitive) +- Use `--debug` to see the raw enum value coming from the provider + +### Date field is wrong format +- Ensure `typecast` step is present — it handles date/datetime conversions +- Ensure the schema field type is `datetime_string`, not `string` + +### Nested object is empty +- Do NOT use `objectMapping` — it is not supported by the validator +- Use the inline block scalar `expression: |` pattern instead +- In `group_data` context, confirm the step ID prefix is correct +```` diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/accounting.md b/.claude/plugins/stackone-unified-builder/references/schemas/accounting.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/accounting.md rename to .claude/plugins/stackone-unified-builder/references/schemas/accounting.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/ats.md b/.claude/plugins/stackone-unified-builder/references/schemas/ats.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/ats.md rename to .claude/plugins/stackone-unified-builder/references/schemas/ats.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/crm.md b/.claude/plugins/stackone-unified-builder/references/schemas/crm.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/crm.md rename to .claude/plugins/stackone-unified-builder/references/schemas/crm.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/documents.md b/.claude/plugins/stackone-unified-builder/references/schemas/documents.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/documents.md rename to .claude/plugins/stackone-unified-builder/references/schemas/documents.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/hris.md b/.claude/plugins/stackone-unified-builder/references/schemas/hris.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/hris.md rename to .claude/plugins/stackone-unified-builder/references/schemas/hris.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/iam.md b/.claude/plugins/stackone-unified-builder/references/schemas/iam.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/iam.md rename to .claude/plugins/stackone-unified-builder/references/schemas/iam.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/lms.md b/.claude/plugins/stackone-unified-builder/references/schemas/lms.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/lms.md rename to .claude/plugins/stackone-unified-builder/references/schemas/lms.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/marketing.md b/.claude/plugins/stackone-unified-builder/references/schemas/marketing.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/marketing.md rename to .claude/plugins/stackone-unified-builder/references/schemas/marketing.md diff --git a/.claude/plugins/stackone-connector-builder/references/schemas/ticketing.md b/.claude/plugins/stackone-unified-builder/references/schemas/ticketing.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/schemas/ticketing.md rename to .claude/plugins/stackone-unified-builder/references/schemas/ticketing.md diff --git a/.claude/plugins/stackone-connector-builder/references/unified-schemas.md b/.claude/plugins/stackone-unified-builder/references/unified-schemas.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/references/unified-schemas.md rename to .claude/plugins/stackone-unified-builder/references/unified-schemas.md diff --git a/.claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/check-connector/SKILL.md similarity index 88% rename from .claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md rename to .claude/plugins/stackone-unified-builder/skills/check-connector/SKILL.md index 7e9f0f3..0a2b1d5 100644 --- a/.claude/plugins/stackone-connector-builder/skills/check-connector/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/check-connector/SKILL.md @@ -18,6 +18,27 @@ Greet with context: --- +## Step 0: Check StackOne provider index + +Before checking the CLI, look up the provider in StackOne's index: + +``` +map_provider_key("{{provider}}") +``` + +If a provider key is returned, save as `provider_key` in session and run: + +``` +get_provider_actions("{{provider_key}}") +``` + +If indexed actions exist, tell the builder: +> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference when building your connector." + +If nothing is found, continue — we'll scaffold from scratch. + +--- + ## Step 1: Detect CLI availability Run: diff --git a/.claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/skills/choose-schema/SKILL.md rename to .claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md diff --git a/.claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/skills/import-schema/SKILL.md rename to .claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md diff --git a/.claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md similarity index 90% rename from .claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md rename to .claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md index d7cc1b4..d9b41f8 100644 --- a/.claude/plugins/stackone-connector-builder/skills/map-fields/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md @@ -22,6 +22,26 @@ If `schema_source` is `"builtin"` and `schema_file` is set in session, also read --- +## Step 0: Check StackOne action coverage + +Before mapping fields, check whether StackOne has coverage data for this provider: + +``` +get_provider_coverage("{{provider_key}}") +``` + +If coverage data exists, use it to pre-populate likely field paths and flag any known gaps. + +Also run a vector search to find similar connectors as reference: + +``` +vector_search("{{schema}} {{provider}} field mapping") +``` + +Use any matching results to inform the fieldConfigs you build. + +--- + ## Step 1: Get the raw provider response Getting a real sample response makes field mapping accurate. diff --git a/.claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md similarity index 100% rename from .claude/plugins/stackone-connector-builder/skills/scope-actions/SKILL.md rename to .claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md diff --git a/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md new file mode 100644 index 0000000..edb6cea --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md @@ -0,0 +1,148 @@ +--- +name: stackone-unified-builder +description: Interactive wizard for building a unified Falcon connector from scratch. Guides external builders through schema selection, connector setup, action scoping, field mapping, validation, and testing. Auto-triggers when someone asks to build a new unified connector, map provider data to a schema, or integrate with a new provider using StackOne's unified API. +invoke: build-unified-connector +--- + +# Build Unified Connector + +End-to-end wizard for building a unified StackOne Falcon connector. + +## Quick Reference + +Run steps in order, or invoke any sub-skill directly to jump to that phase: + +| Step | Command | What it does | +|------|---------|-------------| +| 1 | `/choose-schema` | Pick your schema: built-in (A), import from file (B), or define inline (C) | +| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or any schema document | +| 2 | `/check-connector` | Check if connector exists, pull or scaffold | +| 3 | `/scope-actions` | Decide which resources and operations to expose | +| 4 | `/map-fields` | Map provider API fields to your schema | +| 5 | `/validate-connector` | Validate the YAML configuration | +| 6 | `/test-connector` | Test live against the provider API | + +Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. + +`/import-schema` can be run standalone at any point to load schema fields from a document. It feeds directly into `/map-fields`. + +--- + +## Starting the Wizard + +Check for an existing `.connector-build-session.json`. If found: +> "Found an existing session: +> - **Provider:** `{{provider}}` +> - **Schema:** `{{schema}}` +> - **Last step completed:** `{{session_step}}` +> +> Would you like to: +> - **Resume** from `{{session_step}}` +> - **Restart** from the beginning (clears session)" + +If no session exists, greet the builder and proceed to Step 1: +> "Welcome to the StackOne unified connector builder. I'll guide you through building a connector that maps provider data to a standardised schema step by step." + +--- + +## Step 1 — Choose Schema + +Execute the full `/choose-schema` skill logic. + +**Outcome saved to session:** +- `provider` — the provider name (e.g., `bamboohr`) +- `schema` — the target category or `custom` +- `schema_source` — `builtin`, `imported`, or `custom` +- `resources` — list of resources to build +- `schema_fields` — field definitions (for custom/imported schemas) +- `schema_file` — path to per-category schema reference (for builtin) + +--- + +## Step 2 — Check Connector + +Execute the full `/check-connector` skill logic. + +**Outcome saved to session:** +- `cli_available` — whether the StackOne CLI is usable +- `connector_exists` — whether a base config was pulled from the index +- `connector_path` — where the config lives +- `auth_type` — the authentication method + +--- + +## Step 3 — Scope Actions + +Execute the full `/scope-actions` skill logic. + +**Outcome saved to session:** +- `action_scope` — `{ resource: [actions] }` map +- `use_case` — builder's description of their goal +- `known_limitations` — any flagged gaps + +--- + +## Step 4 — Map Fields + +Execute the full `/map-fields` skill logic. + +**Outcome:** Partial YAML files written to `src/configs/{{provider}}/` + +--- + +## Step 5 — Validate + +Execute the full `/validate-connector` skill logic. + +**Outcome saved to session:** +- `validated: true` + +--- + +## Step 6 — Test + +Execute the full `/test-connector` skill logic. + +**Outcome saved to session:** +- `tested: true` +- `completed_at` + +--- + +## Session File Schema + +`.connector-build-session.json` lives at the project root and carries all state between steps: + +```json +{ + "provider": "bamboohr", + "schema": "hris", + "schema_source": "builtin", + "schema_file": "references/schemas/hris.md", + "schema_fields": [], + "resources": ["employees", "time_off"], + "cli_available": true, + "connector_exists": false, + "connector_path": "src/configs/bamboohr", + "auth_type": "api_key", + "action_scope": { + "employees": ["list", "get"], + "time_off": ["list", "get", "create"] + }, + "use_case": "Sync HR data into customer's internal system", + "known_limitations": [], + "validated": false, + "tested": false, + "session_step": "map-fields", + "completed_at": null +} +``` + +--- + +## Rules + +- Do not write YAML files until Step 4 (`map-fields`) +- Do not run live tests until Step 5 (`validate-connector`) has passed +- Do not delete the session file unless the builder explicitly requests a reset +- Always read session context at the start of each step — never ask for information already in the session diff --git a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md new file mode 100644 index 0000000..8d1d22f --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md @@ -0,0 +1,144 @@ +--- +name: test-connector +description: Step 6 (final) of building a unified connector. Runs live action tests against the provider API, verifies field mapping output matches the schema, and surfaces failures with actionable fix guidance. +invoke: test-connector +--- + +# Test Connector + +Step 6 — the final step of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. + +If `validated` is not `true`, warn: +> "The connector hasn't been validated yet. Run `/validate-connector` first — it catches structural issues before you spend time on live tests." + +If `cli_available` is `false`: +> "The StackOne CLI isn't available, so I can't run automated tests. I'll guide you through manual verification instead." + +--- + +## Step 1: Credentials check + +Ask before running anything: +> "Do you have credentials for `{{provider}}` ready in your `.env` file? +> +> You'll need a line like: +> ``` +> {{PROVIDER_CREDENTIAL_NAME}}=your_value_here +> ``` +> +> The exact variable name is in `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` — look in the `authentication` block for the `args` key names." + +Wait for confirmation before proceeding. + +--- + +## Step 2: Test each action + +Work through each resource and action in `action_scope`. Test in this order: `list` → `get` → `create` → `update` → `delete`. + +**If MCP tools are accessible:** + +Use `test_actions` for automated testing: +``` +test_actions({ + provider: "{{provider}}", + actions: ["unified_list_{{resource}}", "unified_get_{{resource}}"] +}) +``` + +Poll with `get_test_actions_task_status(taskId)` until complete. + +**If running manually via CLI:** +```bash +npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} +``` + +--- + +## Step 3: Verify output for each action + +**For `list` actions:** +- Does the response contain a `data` array? +- Does each item in `data` have all required schema fields populated (non-null)? +- If the provider has multiple pages — is there a `next_cursor` or `next_page` token in the response? +- Are there any unexpected null fields that should have values? + +**For `get` actions:** +- Single object returned with the correct schema shape? +- Does the `id` field match what was requested? + +**For `create` actions:** +- Record created successfully? +- Does the response contain the new record's `id`? + +**For `update` actions:** +- Changes reflected in the response? + +**For `delete` actions:** +- Returns success (200 or 204)? + +--- + +## Step 4: Diagnose and fix failures + +**Field is null or missing in output:** +> "Field `{{field}}` is missing. Let's debug: +> 1. Run with `--debug` to see the raw provider response: `npx @stackone/cli test {{provider}} {{action}} --debug` +> 2. Find `{{field}}` in the raw response — what is the actual JSONPath? +> 3. Update the `expression` in `fieldConfigs` to match." + +Show the fix in the partial YAML and re-test. + +**Enum value passes through unmapped:** +> "The value `{{value}}` for `{{field}}` isn't in the `enumMapper`. Add it:" +```yaml +enumMapper: + {{value}}: {{schema_value}} +``` + +**Pagination not advancing:** +> "The next page isn't being fetched. Check: +> 1. What pagination style does `{{provider}}` use? (page number, cursor token, or link header) +> 2. In the raw response, where does the 'next page' value live? +> 3. Update the `cursor.responsePath` in the action to point to that field." + +**Authentication error (401/403):** +> "Authentication failed. Check: +> 1. Is the `.env` variable name correct? +> 2. Does the `authentication` block reference it correctly? +> 3. Is the credential still valid (not expired)?" + +**404 / endpoint not found:** +> "The API endpoint returned 404. Verify `entrypointUrl` against the provider's API docs — the path may be wrong or require a different base URL." + +After each fix, re-test the affected action. Repeat until all actions pass. + +--- + +## Step 5: Final summary + +When all actions pass: +> "All tests passed! ✓ +> +> **Connector summary:** +> +> | Resource | Actions | Status | +> |----------|---------|--------| +> | `{{resource1}}` | {{actions1}} | ✓ | +> | `{{resource2}}` | {{actions2}} | ✓ | +> +> **Next steps:** +> 1. Commit your connector: `git add src/configs/{{provider}}/ && git commit -m 'feat: add {{provider}} unified connector'` +> 2. If you'd like StackOne to index this connector, open a pull request to the connectors repository. +> 3. Test end-to-end with a real integration account." + +**After all tests pass, always run:** +``` +scramble_credentials("{{provider}}") +``` + +Save `"tested": true` and `"completed_at": "{{datetime}}"` to session. diff --git a/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md new file mode 100644 index 0000000..514f3e4 --- /dev/null +++ b/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md @@ -0,0 +1,114 @@ +--- +name: validate-connector +description: Step 5 of building a unified connector. Runs stackone validate on the connector config, interprets any errors with clear fixes, re-runs until clean, and confirms structural correctness before live testing. +invoke: validate-connector +--- + +# Validate Connector + +Step 5 of the unified connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Validating connector at `src/configs/{{provider}}/`" + +If `connector_path` is missing: +> "No connector path in session. Run `/check-connector` first." + +Read `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the error table. + +--- + +## Step 1: Run validation + +**If `cli_available` is true:** +```bash +npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.yaml +``` + +**If `cli_available` is false (manual review):** + +Read each `.yaml` and `.partial.yaml` file in `src/configs/{{provider}}/` and check: +- All unified actions have `schemaType: unified` +- All unified actions have a `fieldConfigs` array +- All unified actions have all three steps: `request`, `map_fields` (version 2), `typecast` (version 2) +- `list` actions have a `cursor` block +- `get` actions reference `${inputs.id}` in the URL and have `id` in `inputs` +- No typos in `actionType` values (valid: `custom`, `list`, `get`, `create`, `update`, `delete`) +- All `$ref` paths in the main connector point to files that exist +- No tabs — use 2-space indentation throughout + +--- + +## Step 2: Interpret and fix errors + +For each validation error, explain what it means and show the exact fix. + +**Missing `entrypointUrl`:** +> "The action is missing an endpoint URL. Add `entrypointUrl` pointing to the provider's API path:" +```yaml +entrypointUrl: "/{{resource}}" +entrypointHttpMethod: GET +``` + +**Wrong `actionType`:** +> "The actionType value is invalid. Valid values are: `custom`, `list`, `get`, `create`, `update`, `delete`" + +**Missing `fieldConfigs` on unified action:** +> "Unified actions require a `fieldConfigs` array for field mapping. Run `/map-fields` if you haven't mapped fields yet, or add the array:" +```yaml +fieldConfigs: + - targetFieldKey: id + expression: "$.id" +``` + +**Missing steps on unified action:** +> "Unified actions need all three pipeline steps:" +```yaml +steps: + - type: request + id: fetch + - type: map_fields + version: 2 + id: map + - type: typecast + version: 2 + id: cast +``` + +**Missing `schemaType`:** +> "Add `schemaType: unified` to the action." + +**Bad `$ref` path:** +> "The partial file path in `$ref` doesn't exist. Check the filename matches exactly — naming convention is `{{provider}}.{{resource}}.s1.partial.yaml`" + +After each fix, re-run validation. Repeat until it passes cleanly. + +--- + +## Step 3: Confirm clean validation + +When validation passes (or manual review finds no issues): +> "Validation passed — your connector config is structurally correct. ✓ +> +> **Summary:** +> - Provider: `{{provider}}` +> - Resources: {{resources}} +> - Actions: {{action_scope}} +> - Config path: `src/configs/{{provider}}/`" + +Save `"validated": true` to session. + +--- + +## Handoff + +> "Ready for live testing. +> +> Next step: test your actions against the real `{{provider}}` API. +> Run `/test-connector` to continue. +> +> Before running tests, make sure your `.env` file has the right credentials." + +Update `session_step` to `"test-connector"` in the session file. diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..5b6382c --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "mcp__stackone-agent-global__get_docs", + "mcp__stackone-agent-global__get_stackone_categories", + "mcp__stackone-agent-global__map_provider_key", + "mcp__stackone-agent-global__web_search", + "mcp__stackone-agent-global__fetch", + "mcp__stackone-agent-global__vector_search", + "Bash(grep:*)", + "Bash(stackone validate:*)" + ] + } +} diff --git a/src/configs/ashby/ashby.candidates.s1.partial.yaml b/examples/ashby/ashby.candidates.s1.partial.yaml similarity index 100% rename from src/configs/ashby/ashby.candidates.s1.partial.yaml rename to examples/ashby/ashby.candidates.s1.partial.yaml diff --git a/src/configs/ashby/ashby.connector.s1.yaml b/examples/ashby/ashby.connector.s1.yaml similarity index 100% rename from src/configs/ashby/ashby.connector.s1.yaml rename to examples/ashby/ashby.connector.s1.yaml diff --git a/src/configs/bamboo-hr/bamboo-hr.connector.s1.yaml b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml similarity index 97% rename from src/configs/bamboo-hr/bamboo-hr.connector.s1.yaml rename to examples/bamboo-hr/bamboo-hr.connector.s1.yaml index 401c6e2..f37b661 100644 --- a/src/configs/bamboo-hr/bamboo-hr.connector.s1.yaml +++ b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml @@ -40,4 +40,5 @@ authentication: resources: https://documentation.bamboohr.com/reference -$ref: bamboo-hr.employees +actions: + $ref: bamboo-hr.employees diff --git a/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml new file mode 100644 index 0000000..14c8816 --- /dev/null +++ b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml @@ -0,0 +1,640 @@ +- actionId: list_employees + label: List Employees + categories: + - hris + actionType: list + description: Returns a paginated list of employees from BambooHR using the employee directory endpoint. + details: Lists all employees with basic unified schema fields including ID, name, contact information, and employee number. Uses BambooHR's directory endpoint which returns a subset of fields. For full field detail including date of birth, address, and employment history, use get_employee. + resources: https://documentation.bamboohr.com/reference/get-employees-list + entrypointUrl: /employees/directory + entrypointHttpMethod: get + + cursor: + enabled: true + pageSize: 50 + + steps: + - stepId: get_employees + description: Fetch all employees from BambooHR employee directory + stepFunction: + functionName: request + parameters: + url: /employees/directory + method: get + args: + - name: Accept + value: application/json + in: headers + + - stepId: map_employees_data + description: Map employee directory fields to unified HRIS schema + stepFunction: + functionName: map_fields + version: '2' + parameters: + fields: + - targetFieldKey: id + expression: $.id + type: string + - targetFieldKey: first_name + expression: $.firstName + type: string + - targetFieldKey: last_name + expression: $.lastName + type: string + - targetFieldKey: personal_email + expression: $.homeEmail + type: string + - targetFieldKey: personal_phone_number + expression: $.mobilePhone + type: string + - targetFieldKey: employee_number + expression: $.employeeNumber + type: string + - targetFieldKey: title + expression: $.prefix + type: string + dataSource: $.steps.get_employees.output.data.employees + + - stepId: typecast_employees_data + description: Apply unified schema types to employee list data + stepFunction: + functionName: typecast + version: '2' + parameters: + fields: + - targetFieldKey: id + type: string + - targetFieldKey: first_name + type: string + - targetFieldKey: last_name + type: string + - targetFieldKey: personal_email + type: string + - targetFieldKey: personal_phone_number + type: string + - targetFieldKey: employee_number + type: string + - targetFieldKey: title + type: string + dataSource: $.steps.map_employees_data.output.data + + result: + data: $.steps.typecast_employees_data.output.data + +- actionId: get_employee + label: Get Employee + categories: + - hris + actionType: get + description: Returns a single employee record by ID with full unified HRIS schema field mapping, including bank details and employment history. + details: Makes three parallel-style API calls — main employee fields, directDeposit table for all bank accounts, and jobInfo table for full employment history — then combines them into a single unified response. Bank details and employment history are fetched as complete arrays in one call each. 404s on table endpoints are handled gracefully and return empty arrays. + resources: https://documentation.bamboohr.com/reference/get-employee + entrypointUrl: /employees/${inputs.id} + entrypointHttpMethod: get + + inputs: + - name: id + description: The unique identifier of the employee to retrieve + type: string + in: path + required: true + + steps: + - stepId: fetch_employee + description: Fetch core employee fields from BambooHR + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id} + method: get + args: + - name: Accept + value: application/json + in: headers + - name: fields + value: id,firstName,lastName,dateOfBirth,homeEmail,terminationDate,mobilePhone,hireDate,address1,address2,city,zipCode,country,employmentHistoryStatus,employeeNumber,maritalStatus,prefix,ssn + in: query + + - stepId: fetch_bank_details + description: Fetch all bank account (direct deposit) records for the employee + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/tables/directDeposit + method: get + args: + - name: Accept + value: application/json + in: headers + customErrors: + - receivedStatus: 404 + targetStatus: 200 + message: No bank details found for this employee + + - stepId: fetch_employment_history + description: Fetch full employment history (job info) records for the employee + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/tables/jobInfo + method: get + args: + - name: Accept + value: application/json + in: headers + customErrors: + - receivedStatus: 404 + targetStatus: 200 + message: No employment history found for this employee + + - stepId: group_employee_data + description: Combine core employee data, bank details, and employment history into a single object + stepFunction: + functionName: group_data + parameters: + stepsDataToGroup: + - fetch_employee + - fetch_bank_details + - fetch_employment_history + isSingleRecord: true + + - stepId: map_employee_data + description: Map combined employee data to unified HRIS schema fields + stepFunction: + functionName: map_fields + version: '2' + parameters: + dataSource: $.steps.group_employee_data.output.data + fields: + - targetFieldKey: id + expression: $.fetch_employee.id + type: string + - targetFieldKey: first_name + expression: $.fetch_employee.firstName + type: string + - targetFieldKey: last_name + expression: $.fetch_employee.lastName + type: string + - targetFieldKey: date_of_birth + expression: $.fetch_employee.dateOfBirth + type: string + - targetFieldKey: personal_email + expression: $.fetch_employee.homeEmail + type: string + - targetFieldKey: termination_date + expression: $.fetch_employee.terminationDate + type: string + - targetFieldKey: personal_phone_number + expression: $.fetch_employee.mobilePhone + type: string + - targetFieldKey: start_date + expression: $.fetch_employee.hireDate + type: string + - targetFieldKey: home_location + type: object + expression: | + { + "street_1": $.fetch_employee.address1, + "street_2": $.fetch_employee.address2, + "city": $.fetch_employee.city, + "zip_code": $.fetch_employee.zipCode, + "country": $.fetch_employee.country + } + - targetFieldKey: employments + type: object + expression: $.fetch_employment_history.data + - targetFieldKey: employee_number + expression: $.fetch_employee.employeeNumber + type: string + - targetFieldKey: marital_status + expression: $.fetch_employee.maritalStatus + type: string + - targetFieldKey: national_identity_numbers + type: object + expression: | + [{ + "value": $.fetch_employee.ssn, + "type": "ssn" + }] + - targetFieldKey: bank_details + type: object + expression: $.fetch_bank_details.data + - targetFieldKey: title + expression: $.fetch_employee.prefix + type: string + + - stepId: typecast_employee_data + description: Apply unified schema types to combined employee data + stepFunction: + functionName: typecast + version: '2' + parameters: + dataSource: $.steps.map_employee_data.output.data + fields: + - targetFieldKey: id + type: string + - targetFieldKey: first_name + type: string + - targetFieldKey: last_name + type: string + - targetFieldKey: date_of_birth + type: datetime_string + - targetFieldKey: personal_email + type: string + - targetFieldKey: termination_date + type: datetime_string + - targetFieldKey: personal_phone_number + type: string + - targetFieldKey: start_date + type: datetime_string + - targetFieldKey: employee_number + type: string + - targetFieldKey: marital_status + type: string + - targetFieldKey: title + type: string + + result: + data: $.steps.typecast_employee_data.output.data + +- actionId: create_employee + label: Create Employee + categories: + - hris + actionType: create + description: Creates a new employee record in BambooHR. + details: Creates a new employee with the provided personal and contact details. First name and last name are required. All other fields are optional and will only be sent if provided. + resources: https://documentation.bamboohr.com/reference/post-employee + entrypointUrl: /employees + entrypointHttpMethod: post + + inputs: + - name: first_name + description: The employee's first name + type: string + in: body + required: true + - name: last_name + description: The employee's last name + type: string + in: body + required: true + - name: date_of_birth + description: The employee's date of birth in YYYY-MM-DD format + type: datetime_string + in: body + required: false + - name: personal_email + description: The employee's personal email address + type: string + in: body + required: false + - name: personal_phone_number + description: The employee's personal mobile phone number + type: string + in: body + required: false + - name: start_date + description: The employee's hire date in YYYY-MM-DD format + type: datetime_string + in: body + required: false + - name: title + description: The employee's name prefix (Mr, Ms, Dr, etc.) + type: string + in: body + required: false + - name: employee_number + description: The assigned employee number or ID + type: string + in: body + required: false + - name: marital_status + description: The employee's marital status + type: string + in: body + required: false + - name: home_location + description: The employee's home address details + type: object + in: body + required: false + properties: + - name: street_1 + description: First line of the home address + type: string + required: false + - name: street_2 + description: Second line of the home address + type: string + required: false + - name: city + description: City of the home address + type: string + required: false + - name: zip_code + description: ZIP or postal code of the home address + type: string + required: false + - name: country + description: Country of the home address + type: string + required: false + + steps: + - stepId: create_employee + description: Create new employee record in BambooHR + stepFunction: + functionName: request + parameters: + url: /employees + method: post + args: + - name: Accept + value: application/json + in: headers + - name: Content-Type + value: application/json + in: headers + - name: firstName + value: $.inputs.first_name + in: body + - name: lastName + value: $.inputs.last_name + in: body + - name: dateOfBirth + value: $.inputs.date_of_birth + in: body + condition: '{{present(inputs.date_of_birth)}}' + - name: homeEmail + value: $.inputs.personal_email + in: body + condition: '{{present(inputs.personal_email)}}' + - name: mobilePhone + value: $.inputs.personal_phone_number + in: body + condition: '{{present(inputs.personal_phone_number)}}' + - name: hireDate + value: $.inputs.start_date + in: body + condition: '{{present(inputs.start_date)}}' + - name: prefix + value: $.inputs.title + in: body + condition: '{{present(inputs.title)}}' + - name: employeeNumber + value: $.inputs.employee_number + in: body + condition: '{{present(inputs.employee_number)}}' + - name: maritalStatus + value: $.inputs.marital_status + in: body + condition: '{{present(inputs.marital_status)}}' + - name: address1 + value: $.inputs.home_location.street_1 + in: body + condition: '{{present(inputs.home_location.street_1)}}' + - name: address2 + value: $.inputs.home_location.street_2 + in: body + condition: '{{present(inputs.home_location.street_2)}}' + - name: city + value: $.inputs.home_location.city + in: body + condition: '{{present(inputs.home_location.city)}}' + - name: zipCode + value: $.inputs.home_location.zip_code + in: body + condition: '{{present(inputs.home_location.zip_code)}}' + - name: country + value: $.inputs.home_location.country + in: body + condition: '{{present(inputs.home_location.country)}}' + + result: + message: Employee created successfully + data: + id: $.steps.create_employee.output.data.id + +- actionId: update_employee + label: Update Employee + categories: + - hris + actionType: update + description: Updates an existing employee record in BambooHR. + details: Updates one or more fields on an existing employee. Only provided fields will be updated — all body fields are optional except the employee ID path parameter. BambooHR uses POST (not PATCH) for employee field updates. + resources: https://documentation.bamboohr.com/reference/update-employee + entrypointUrl: /employees/${inputs.id} + entrypointHttpMethod: post + + inputs: + - name: id + description: The unique identifier of the employee to update + type: string + in: path + required: true + - name: first_name + description: The employee's first name + type: string + in: body + required: false + - name: last_name + description: The employee's last name + type: string + in: body + required: false + - name: date_of_birth + description: The employee's date of birth in YYYY-MM-DD format + type: datetime_string + in: body + required: false + - name: personal_email + description: The employee's personal email address + type: string + in: body + required: false + - name: personal_phone_number + description: The employee's personal mobile phone number + type: string + in: body + required: false + - name: start_date + description: The employee's hire date in YYYY-MM-DD format + type: datetime_string + in: body + required: false + - name: termination_date + description: The employee's termination date in YYYY-MM-DD format + type: datetime_string + in: body + required: false + - name: title + description: The employee's name prefix (Mr, Ms, Dr, etc.) + type: string + in: body + required: false + - name: employee_number + description: The assigned employee number or ID + type: string + in: body + required: false + - name: marital_status + description: The employee's marital status + type: string + in: body + required: false + - name: home_location + description: The employee's home address details + type: object + in: body + required: false + properties: + - name: street_1 + description: First line of the home address + type: string + required: false + - name: street_2 + description: Second line of the home address + type: string + required: false + - name: city + description: City of the home address + type: string + required: false + - name: zip_code + description: ZIP or postal code of the home address + type: string + required: false + - name: country + description: Country of the home address + type: string + required: false + + steps: + - stepId: update_employee + description: Update employee record fields in BambooHR + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id} + method: post + args: + - name: Accept + value: application/json + in: headers + - name: Content-Type + value: application/json + in: headers + - name: firstName + value: $.inputs.first_name + in: body + condition: '{{present(inputs.first_name)}}' + - name: lastName + value: $.inputs.last_name + in: body + condition: '{{present(inputs.last_name)}}' + - name: dateOfBirth + value: $.inputs.date_of_birth + in: body + condition: '{{present(inputs.date_of_birth)}}' + - name: homeEmail + value: $.inputs.personal_email + in: body + condition: '{{present(inputs.personal_email)}}' + - name: mobilePhone + value: $.inputs.personal_phone_number + in: body + condition: '{{present(inputs.personal_phone_number)}}' + - name: hireDate + value: $.inputs.start_date + in: body + condition: '{{present(inputs.start_date)}}' + - name: terminationDate + value: $.inputs.termination_date + in: body + condition: '{{present(inputs.termination_date)}}' + - name: prefix + value: $.inputs.title + in: body + condition: '{{present(inputs.title)}}' + - name: employeeNumber + value: $.inputs.employee_number + in: body + condition: '{{present(inputs.employee_number)}}' + - name: maritalStatus + value: $.inputs.marital_status + in: body + condition: '{{present(inputs.marital_status)}}' + - name: address1 + value: $.inputs.home_location.street_1 + in: body + condition: '{{present(inputs.home_location.street_1)}}' + - name: address2 + value: $.inputs.home_location.street_2 + in: body + condition: '{{present(inputs.home_location.street_2)}}' + - name: city + value: $.inputs.home_location.city + in: body + condition: '{{present(inputs.home_location.city)}}' + - name: zipCode + value: $.inputs.home_location.zip_code + in: body + condition: '{{present(inputs.home_location.zip_code)}}' + - name: country + value: $.inputs.home_location.country + in: body + condition: '{{present(inputs.home_location.country)}}' + + result: + message: Employee updated successfully + data: + id: $.inputs.id + +- actionId: terminate_employee + label: Terminate Employee + categories: + - hris + actionType: delete + description: Terminates an employee in BambooHR by setting their termination date. + details: BambooHR does not support hard deletion of employee records. This action terminates an employee by setting their termination date. The employee record remains accessible in the system after termination. + resources: https://documentation.bamboohr.com/reference/update-employee + entrypointUrl: /employees/${inputs.id} + entrypointHttpMethod: post + + inputs: + - name: id + description: The unique identifier of the employee to terminate + type: string + in: path + required: true + - name: termination_date + description: The termination date in YYYY-MM-DD format + type: datetime_string + in: body + required: true + + steps: + - stepId: terminate_employee + description: Set termination date on employee record in BambooHR + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id} + method: post + args: + - name: Accept + value: application/json + in: headers + - name: Content-Type + value: application/json + in: headers + - name: terminationDate + value: $.inputs.termination_date + in: body + + result: + message: Employee terminated successfully + data: + id: $.inputs.id diff --git a/src/configs/README.md b/src/configs/README.md deleted file mode 100644 index 76f554b..0000000 --- a/src/configs/README.md +++ /dev/null @@ -1,792 +0,0 @@ -# Building a Falcon Connector - -Environment setup and developer information, see internal Notion pages. - -Connectors can contain both unified and non-unified actions. - -- Unified actions must take inputs defined in the StackOne schema and then provide a response output to the defined StackOne schema for the respective resource. -- Non-unified actions map exactly to the underlying provider's API call and output the provider's response in its entirety. No mapping of the output data is performed. - -**By default only generate non-unified actions unless explictly told otherwise** - -# File Structure & Organization - -## Directory Structure - -``` -src/configs/{connector-name}/ -├── {connector-name}.connector.s1.yaml # Main connector file with authentication config -└── {connector-name}.{resource}.s1.partial.yaml # For each provider resource that contains respective actions -``` - -## File Naming Convention - -- Use kebab-case for file names -- **Do not create a single connector file with all the actions**. Generate partial files for each resource. -- Reference partial configs in main file using `$ref: connector-name.resource`. Add the references in alphabetical order. - -**IMPORTANT: Unless explicitly specified otherwise, ALL actions should be configured as non-unified.** - -# YAML Structure - -## Complete Example - -Here's a minimal complete connector showing all main sections: - -```yaml -StackOne: 1.0.0 -info: - title: Provider Name - key: provider_name - version: 1.0.0 - assets: - icon: https://stackone-logos.com/api/provider_name/filled/png - description: Brief description of the provider - -baseUrl: https://api.provider.com/v1 - -authentication: - - custom: - type: custom - label: API Key - authorization: - type: bearer - token: $.credentials.apiKey - configFields: - - key: apiKey - label: API Key - type: password - required: true - secret: true - environments: - - key: production - name: Production - -actions: - - actionId: list_users - categories: - - hris - actionType: list - schema: users - description: Get list of users - resources: https://api.provider.com/docs/users/list - steps: - - stepId: fetch_users - stepFunction: - functionName: request - parameters: - url: /users - method: get - result: - data: $.steps.fetch_users.output.data -``` - -## YAML Best Practices - -### Reserved Characters - -**⚠️ IMPORTANT: Never use the `:` character as a value in YAML files.** - -The colon (`:`) is a reserved character in YAML syntax used to separate keys from values. Using it as a literal value can cause parsing errors or unexpected behavior. - -**Incorrect:** - -```yaml -description: Filter by status: pending, approved, denied, cancelled -``` - -**Correct:** - -```yaml -description: Filter by status (pending, approved, denied, cancelled) -``` - -If you need to include a colon in a description or other text value, use alternative wording or rephrase the text to avoid the colon character. - -## Meta Info - -The `info` section contains metadata about the connector: - -```yaml -StackOne: 1.0.0 -info: - title: Jira # Provider display name - key: jira # Unique provider identifier (lowercase) - version: 1.0.0 # Connector version - assets: - icon: https://stackone-logos.com/api/jira/filled/png - description: Jira is a project management and issue tracking tool that helps teams plan, track, and manage their work. - -baseUrl: https://api.provider.com/v1 # Base URL for all API calls - -# Optional: Rate limiting configuration -rateLimit: - mainRatelimit: 10 # Requests per second - -# Documentation context -resources: "Provider API documentation: https://api.provider.com/docs" -``` - -## Authentication - -- `setupFields` T1 facing account credential fields. Generally information about OAuth apps and multi-tenant provider credentials e.g. Client ID, Client Secret, scopes. -- `configFields` T2 facing account credential fields. Generally end user credential information e.g. API token, user specific sub-domain. -- `label` Allowed values: `API Key`, `OAuth 2.0`. Only include additional text if absolutley necessary to differentiate the different auth types available. If additional text is needed then keep it to a bare minimum. - -### API Key - -```yaml -authentication: - - custom: - type: custom - label: API Key - support: - link: https://hub.stackone.com/connection-guides/ticketing/jira - description: The steps in this guide require Admin privileges within Jira. - authorization: - type: basic - username: $.credentials.email - password: $.credentials.accessToken - configFields: - - key: accessToken - label: API Token - type: password - required: true - secret: true - placeholder: ATATT3xFfGF0aQNaJZQ9JtSvQ_example - description: You can generate an API Token by going to your Atlassian Account Settings > Security > Create and manage API tokens. - tooltip: Make sure to save your API token securely as it won't be shown again. - environments: - - key: production - name: Production - testActionsIds: - - list_users -``` - -### OAuth - -Typical OAuth 2 configuration (Authorization code > Access Token > Refresh Token) examples can be seen in the following connectors: - -- asana -- gmail -- xero - -Complex examples: - -- jira - also includes a post-authentication call to retrieve and save the `cloud ID` needed to make API requests. - -```yaml -authentication: - - oauth2: - type: oauth2 - label: OAuth 2.0 - support: - link: https://docs.stackone.com/integration-guides/accounting/xero - description: Login to your Xero account to connect your data. - setupFields: - - key: clientId - label: Client ID - type: text - required: true - secret: false - placeholder: 4A98C887DABEBDF0BD8EF7D - description: Can be found by going to [Xero Developer portal](https://developer.xero.com/myapps/) > My Apps > Select your app > Configuration > Client ID - tooltip: The Client ID from your Xero app registration. - - key: clientSecret - label: Client Secret - type: password - required: true - secret: true - placeholder: PcUXs2dse1e46_Lfyu-OKHKjDsRaHyEhBGEGXgs_ - description: Use an existing client secret, otherwise one can be generated by going to [Xero Developer portal](https://developer.xero.com/myapps/) > My Apps > Select your app > Configuration - tooltip: The Client Secret from your Xero app registration. - - key: scopes - label: Scopes - type: text - required: false - secret: false - placeholder: offline_access accounting.transactions accounting.settings.read accounting.journals.read - description: Enter a list of valid Xero scopes separated by a space. See [Xero API scopes](https://developer.xero.com/documentation/guides/oauth2/scopes/) for more information. If no scopes are provided, the default scopes will be used. - authorization: - type: oauth2 - # Start Authorization request block - authorizationUrl: https://login.xero.com/identity/connect/authorize - authorizationParams: - response_type: "code" - client_id: $.credentials.clientId - redirect_uri: "${apiHostUri}/connect/oauth2/xero/callback" - scope: $.credentials.scopes - state: "stackone" - # End Authorization request block - tokenUrl: https://identity.xero.com/connect/token - tokenExpiresIn: 1800 - tokenRefreshExpiresIn: 3600 - token: $.credentials.accessToken - includeBearer: true - refreshAuthentication: - action: - actionId: refresh_token_xero - categories: - - internal - actionType: refresh_token - label: Refresh Token - description: Refresh Xero OAuth2 token - steps: - - stepId: refresh_token_request - description: Get new access token - stepFunction: - functionName: request - parameters: - baseUrl: "https://identity.xero.com" - url: "/connect/token" - method: post - authorization: - type: basic - username: $.credentials.clientId - password: $.credentials.clientSecret - encoding: base64 - args: - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: grant_type - value: refresh_token - in: body - - name: refresh_token - value: $.credentials.refreshToken - in: body - - stepId: map_tokens - description: Map tokens data - stepFunction: - functionName: map_fields - version: "2" - parameters: - fields: - - targetFieldKey: accessToken - expression: $.access_token - type: string - - targetFieldKey: refreshToken - expression: $.refresh_token - type: string - - targetFieldKey: expiresIn - expression: $.expires_in - type: number - - targetFieldKey: refreshExpiresIn - expression: $.refresh_expires_in - type: number - dataSource: $.steps.refresh_token_request.output.data - result: - data: $.steps.map_tokens.output.data - environments: - - key: production - name: Production -``` - -## Actions - -Actions contain these main parts: - -- `categories` - list of categories this action will appear under in the StackOne UI. -- `actionType` - can only be one of the following values: list, get, create, update, delete, custom, refresh_token. If the provider does not use cursor based pagination then instead of using list, set this value to get. -- `context` - Documentation URLs or notes specific to this action. If adding a URL do not use base documentation URL, be specific and link only the page related to this action. -- Entry Point - - `entrypointUrl` - the url endpoint for this action to be routed from - - `entrypointHttpMethod` -- `inputs` - input request body definition for the action that is used to capture any data from the incoming request that will be needed in the proceeding steps. Path, query and body parameters can be mapped to the `inputs` object. These values can then be referenced in the connector config by using `'{{input.[name]}}'` - - `input.type` can only have the following values: string, number, boolean, datetime_string, object - - `input.description` is required and MUST be added. This description is to give context for the user to understand what values are expected. If the values are defined in an enum, you must list all possible values if there is no ISO standard. - - The `input.type: object` allows the use of the `properties` key which can contain nested entries of input fields. - - **For non-unified actions, the inputs must match exactly the full provider's request parameters for headers, query, path and body. DO NOT CREATE INPUTS THAT DO NOT EXIST IN THE PROVIDER API** -- `steps` - List of step functions to execute -- `result` - Final action output response - -```yaml -actions: - - actionId: get_user - categories: - - ticketing - actionType: get - schema: users - description: Get Users - resources: https://api.provider.com/docs/users/get, https://api.provider.com/guides/user-management - entrypointUrl: /users/:id - entrypointHttpMethod: get - inputs: - - name: id - description: User ID - type: string - in: path - required: true - fieldConfigs: - - targetFieldKey: id - expression: $.accountId - type: string - - targetFieldKey: type - expression: $.accountType - type: enum - enumMapper: - matcher: - - matchExpression: '{{$.accountType == "atlassian"}}' - value: agent - - matchExpression: '{{$.accountType == "app"}}' - value: bot - - matchExpression: '{{$.accountType == "customer"}}' - value: contact - - targetFieldKey: active - expression: $.active - type: boolean - steps: - ... - result: - ... -``` - -### Field Configs - -NOTE: `fieldConfigs` are NOT required when building non-unified connectors! - -- `fieldConfigs` - Mapping of the provider response fields to StackOne unified response. - - `expression` - The json path selector on the provider response. Also supports [JEXL expressions](https://github.com/StackOneHQ/connect/tree/main/packages/expressions) - - `enumMapper` - Two ways of mapping to known values: - - Can use a built-in matcher to take the evaluated `expression` to a standard value. - - Can use a `matchExpression` condition to map a custom value. - - `type` - Can be one of following values: - - `string`: Text values - - `boolean`: True/false values - - `enum`: Use with enumMapper for value mapping - - `datetime_string`: ISO 8601 date strings - - `number`: Numeric values - -#### Custom Matcher - -Custom enum matchers are used when you need to map provider values to large StackOne enums where explicit `matchExpression` mappings would be impractical. - -**When to use:** - -- Large enums like `DocumentFileFormatEnum` (1200+ values) -- Standard mappings that can be reused across multiple connectors -- Cases where a simple lookup/normalization function handles the mapping - -**Implementation Steps:** - -1. **Create the Enum in `connect/packages/utils/src/shared/[domain]/`** - - Create a new file (e.g., `fileFormats.ts`) with the enum and matcher function: - - ```typescript - export enum DocumentFileFormatEnum { - pdf = "pdf", - doc = "doc", - docx = "docx", - // ... - } - - export const getDocumentFileFormatFromExtension = ( - fileExtension: unknown - ): string | undefined => { - if (typeof fileExtension !== "string" || !fileExtension) { - return undefined; - } - - // Normalize: remove leading dot, lowercase, trim - const normalized = fileExtension.replace(/^\./, "").toLowerCase().trim(); - - // Check if valid enum key - if (normalized in DocumentFileFormatEnum) { - return DocumentFileFormatEnum[ - normalized as keyof typeof DocumentFileFormatEnum - ]; - } - - return undefined; - }; - ``` - -2. **Export from `connect/packages/utils/src/shared/index.ts`** - - ```typescript - export { getDocumentFileFormatFromExtension } from "./documents/fileFormats"; - ``` - -3. **Register in `connect/packages/connect-sdk/src/connectors/schemas.ts`** - - Add the matcher name to the enum list: - - ```typescript - enumMapper: zStrictObject({ - matcher: z - .enum([ - 'country_alpha2code_by_alpha2code', - // ... existing matchers - 'document_file_format_from_extension', // Add here - ]) - ``` - -4. **Implement in `connect/packages/core/src/stepFunctions/mapFields/getEnumMatcher.ts`** - - Import and register the function: - - ```typescript - import { - // ... existing imports - getDocumentFileFormatFromExtension, - } from "@stackone/utils"; - - export const getEnumMatcher = ( - matcher: string - ): ((value: unknown) => unknown) | undefined => { - switch (matcher) { - // ... existing cases - case "document_file_format_from_extension": - return getDocumentFileFormatFromExtension; - default: - return undefined; - } - }; - ``` - -5. (Optional) **Build Connect Packages** - - ```bash - cd connect - npm run build:dev - ``` - -6. **Use in YAML Connector** - - ```yaml - fieldConfigs: - - targetFieldKey: file_format - expression: $.fullFileExtension - type: enum - enumMapper: - matcher: "document_file_format_from_extension" - ``` - -**Example: Document File Format Matcher** - -See the implementation in: - -- Enum: `connect/packages/utils/src/shared/documents/fileFormats.ts` -- Usage: `connectors/src/configs/googledrive/googledrive.files.s1.partial.yaml` - -### Steps - -```yaml -actions: - - actionId: - ... - steps: - - stepId: list_users - description: List users - stepFunction: - functionName: request - parameters: - url: '/users' - method: get - args: - - name: showInactive - value: true - in: body - condition: '{{true}}' - - name: accept - value: application/json - in: headers - condition: '{{true}}' - - name: maxResults - value: '25' - in: query - condition: '{{true}}' - response: - collection: true -``` - -### Result - -Read response - -```yaml -actions: - - actionId: list_users - ... - steps: - ... - result: - data: $.steps.typecast_employees_data.output.data -``` - -Write response - -```yaml -actions: - - actionId: update_users - ... - steps: - ... - result: - message: Employee updated successfully - data: - id: '{{inputs.id}}' -``` - -# Step Functions - -Step functions are defined in the connect repo: - -packages/core/src/stepFunctions/stepFunctionsList.ts - -## Standard Step Functions - -### Request - -Performs an http request and extract the response. - -**IMPORTANT: Always use `args` for request parameters (headers, query params, and body fields). Never use direct `body` field.** - -- Dynamically calculated `args` should have `condition` properties to verify that the values are not undefined otherwise they will be included in the call. E.g. - `condition: '{{present($.input.field)}}'` - -```yaml -stepFunction: - functionName: request - parameters: - url: '/users' - method: get - args: - - name: accept - value: application/json - in: headers - # Body parameters should also use args - - name: email - value: $.inputs.email - in: body - - name: name - value: $.inputs.name - in: body - response: - collection: true - indexField: id - dataKey: user - customErrors: # Optional remapping of provider error responses - - receivedStatus: 404 - targetStatus: 400 - message: 'Custom error message' - condition: "{{headers['connection'] == 'keep-alive'}}" -``` - -### Paginated Request - -If the provider supports cursor based pagination then use `paginated_request` instead of `request` as the `stepFunction`. - -**DO NOT USE `paginated_request` if pagination method is not cursor based. Use the `request` step function instead!** - -- `response.nextKey` should be the field in the provider response that contains the next cursor value -- `iterator` is how the next cursor is mapped into the provider request - -```yaml -stepFunction: - functionName: paginated_request - parameters: - url: "/application.list" - method: post - response: - dataKey: results - nextKey: nextCursor - iterator: - key: cursor - in: body - args: - - name: Content-Type - value: application/json - in: headers -``` - -### Group Data - -```yaml -stepFunction: - functionName: group_data - parameters: - stepsDataToGroup: - - get_all_employees - - get_more_employees_data - isSingleRecord: false -``` - -### Map - -Using the `fieldConfigs` , performs a mapping of the `dataSource` - -NOTE: This step is not required when building a non-unified actions - -```yaml -- stepId: map_employee_data - description: Map employee data - stepFunction: - functionName: map_fields - version: "2" - parameters: - dataSource: $.steps.group_employee_data.output.data -``` - -### Typecast - -Applies the types as defined in `fieldConfigs` - -NOTE: This step is not required when building a non-unified actions - -```yaml -- stepId: typecast_employee_data - description: Typecast employee data - stepFunction: - functionName: typecast - version: "2" - parameters: - dataSource: $.steps.map_employee_data.output.data -``` - -## Custom Step Functions - -_Add guide on creating custom step functions_ - -# Dynamic Values - -Falcon connectors support multiple expression formats for dynamic values. - -**JSONPath is the preferred expression format** and should be used by default unless you are constructing a string or performing conditional logic. - -## Expression Formats - -### 1. JSONPath (`$.path.to.field`) - PREFERRED - -**This is the recommended format for most use cases.** JSONPath provides a consistent, powerful way to access data throughout your connector. - -```yaml -# Access credentials -token: $.credentials.apiKey -username: $.credentials.email - -# Access inputs -value: $.inputs.userId - -# Access step output -dataSource: $.steps.fetch_users.output.data - -# Access array elements -expression: $.users[0].id -``` - -**When to use JSONPath:** - -- When the ENTIRE value is a direct reference (no string construction needed) -- Accessing credentials: `token: $.credentials.apiKey` -- Accessing inputs: `userId: $.inputs.userId` -- Accessing step output: `dataSource: $.steps.fetch_users.output.data` -- Reading response fields: `nextCursor: $.response.pagination.cursor` - -### 2. String Interpolation (`${...}`) - -Use **whenever you need to embed dynamic values within a string** (i.e., string construction): - -```yaml -# Single dynamic value in URL path -url: /users/${inputs.id} -url: /calls/${inputs.callId}/transcript - -# Multiple dynamic values in URL -url: /users/${inputs.id}/posts/${inputs.postId} - -# URLs with dynamic domains -baseUrl: https://${credentials.domain}.api.com - -# Environment variables -redirect_uri: ${apiHostUri}/callback -``` - -**When to use String Interpolation:** - -- **ANY time you're building a string with embedded dynamic values** -- URLs with path parameters: `/resource/${inputs.id}` -- URLs with multiple segments: `/users/${inputs.userId}/posts/${inputs.postId}` -- Domain or subdomain construction: `https://${credentials.subdomain}.api.com` -- Any string concatenation scenario - -### 3. JEXL Expressions (`'{{...}}'`) - -Use only when you need conditional logic, transformations, or complex expressions. Must be wrapped in single quotes: - -```yaml -# Conditionals -condition: '{{present(inputs.includeInactive)}}' -condition: '{{inputs.includeInactive == true}}' -matchExpression: '{{$.accountType == "admin"}}' - -# Transformations -value: '{{inputs.name.toUpperCase()}}' - -# Complex logic -value: '{{$.status == "active" ? "enabled" : "disabled"}}' - -# Ternary operations -value: '{{$.count > 0 ? $.count : "none"}}' -``` - -**When to use JEXL:** - -- Conditional logic in `condition` fields -- Enum matching with `matchExpression` -- String transformations (uppercase, lowercase, etc.) -- Mathematical operations -- Ternary operators - -For more details on JEXL expressions, see the [expressions package documentation](https://github.com/StackOneHQ/connect/tree/main/packages/expressions). - -## Common Use Cases - -**Credential Templating (use JSONPath):** - -```yaml -token: $.credentials.apiKey -username: $.credentials.email -password: $.credentials.password -``` - -**Input Parameters:** - -```yaml -# Direct reference - use JSONPath -value: $.inputs.userId -userId: $.inputs.userId - -# String construction (URLs) - use String Interpolation -url: /users/${inputs.id} -url: /calls/${inputs.callId}/transcript -``` - -**Step References (use JSONPath):** - -```yaml -dataSource: $.steps.stepId.output.data -value: $.steps.fetch_user.output.data.userId -``` - -**Conditions (use JEXL):** - -```yaml -condition: "{{inputs.includeDeleted == true}}" -matchExpression: '{{$.status == "active"}}' -``` - -# Validation - -Validate a stackone connector file via CLI. First install the package: - -```tsx -npm install -g @stackone/cli -``` - -Usage: - -```tsx -stackone validate [pathToYaml] // Single run validation of the file -``` diff --git a/src/configs/bamboo-hr/bamboo-hr.employees.s1.partial.yaml b/src/configs/bamboo-hr/bamboo-hr.employees.s1.partial.yaml deleted file mode 100644 index b13d4d4..0000000 --- a/src/configs/bamboo-hr/bamboo-hr.employees.s1.partial.yaml +++ /dev/null @@ -1,76 +0,0 @@ -actions: - - actionId: list_employees - label: List Employees - categories: - - hris - actionType: custom - description: Lists all employees with unified schema mapping, returning only the employee ID. Uses cursor-based pagination. - resources: https://documentation.bamboohr.com/reference/get-employees-list - - cursor: - enabled: true - pageSize: 50 - - inputs: - - name: page_size - description: Maximum number of employees to return per page (1-100, defaults to 50) - type: number - in: query - required: false - - name: cursor - description: Pagination cursor for fetching the next page of results - type: string - in: query - required: false - - steps: - - stepId: get_employees - description: Fetch employees from BambooHR API - stepFunction: - functionName: request - parameters: - url: /employees - method: get - args: - - name: Accept - value: application/json - in: headers - - name: page[limit] - value: $.inputs.page_size - in: query - condition: "{{present(inputs.page_size)}}" - - name: page[limit] - value: 50 - in: query - condition: "{{!present(inputs.page_size)}}" - - name: page[after] - value: $.inputs.cursor - in: query - condition: "{{present(inputs.cursor)}}" - - - stepId: map_employees_data - description: Map to unified schema - stepFunction: - functionName: map_fields - version: '2' - parameters: - fields: - - targetFieldKey: id - expression: $.employeeId - type: string - dataSource: $.steps.get_employees.output.data.data - - - stepId: typecast_employees_data - description: Apply unified types - stepFunction: - functionName: typecast - version: '2' - parameters: - fields: - - targetFieldKey: id - type: string - dataSource: $.steps.map_employees_data.output.data - - result: - data: $.steps.typecast_employees_data.output.data - next: $.steps.get_employees.output.data.meta.page.nextCursor diff --git a/src/configs/breathehr/breathehr_v1-0-1.s1.yaml b/src/configs/breathehr/breathehr_v1-0-1.s1.yaml new file mode 100644 index 0000000..00377fe --- /dev/null +++ b/src/configs/breathehr/breathehr_v1-0-1.s1.yaml @@ -0,0 +1,2411 @@ +StackOne: 1.0.0 +info: + title: BreatheHR + key: breathehr + version: 1.0.1 + assets: + icon: https://stackone-logos.com/api/breathe/filled/png + description: BreatheHR is a modern HR platform that helps companies streamline their hiring process. +baseUrl: https://api.breathehr.com/v1 +authentication: + - custom: + type: custom + label: Public API + support: + link: https://hub.stackone.com/connection-guides/hris/breathehr + description: The steps in this guide require Admin privileges within BreatheHR. + authorization: + type: none + configFields: + - key: apiKey + label: API Key + type: password + required: true + secret: true + placeholder: 7cef933f17737108009021191848635eb898b1b99cg2ffee227b577024ddfb2b + description: You can generate an API Key in your BreatheHR account **Configure > Settings > Integrations > API Setup**. + environments: + - key: production + name: Production + testActions: + - action: list_employees + required: true +actions: + - actionId: list_employees + categories: + - hris + actionType: list + schema: employees + label: List Employees + description: Get a list of employees + details: "Returns a paginated list of all employees in the BreatheHR system. Supports filtering by department_id, location_id, division_id, employment_status, and pagination with page and per_page. Returns employee data including ID, first_name, last_name, email, employment_status, department, location, and other employee properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + - name: filter + description: Type of employees to return + type: string + in: query + required: false + - name: rotacloud + description: Return leave requests where rotacloud is not excluded from integration + type: boolean + in: query + required: false + steps: + - stepId: get_all_employees + description: Get employees request + stepFunction: + functionName: request + parameters: + url: /employees + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + - name: filter + value: '{{$.inputs.filter}}' + in: query + condition: '{{present($.inputs.filter)}}' + - name: rotacloud + value: '{{$.inputs.rotacloud}}' + in: query + condition: '{{present($.inputs.rotacloud)}}' + response: + collection: true + result: + data: $.steps.get_all_employees.output.data + - actionId: get_employee + categories: + - hris + actionType: get + schema: employees + label: Get Employee + description: Get a specific employee by ID + details: "Returns detailed information for a specific employee by their ID. Includes employee ID, first_name, last_name, email, employment_status, department, location, division, job_title, start_date, and all other employee properties." + inputs: + - name: id + description: The ID of the employee to retrieve. + type: string + in: path + required: true + steps: + - stepId: get_an_employee + description: Get employee request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id} + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.get_an_employee.output.data + - actionId: list_employee_absences + categories: + - hris + actionType: get + label: Get Employee Absences + description: Get absences for a specific employee by ID + details: "Returns a paginated list of absences for a specific employee. Requires employee ID. Supports optional filtering to exclude cancelled absences and pagination with page and per_page parameters. Returns absence data including dates, reasons, and other absence properties." + inputs: + - name: id + description: The ID of the employee whose absences to retrieve. + type: string + in: path + required: true + - name: exclude_cancelled_absences + description: Don't return cancelled leave requests + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_employee_absences + description: Get employee absences request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/absences + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + - name: exclude_cancelled_absences + value: '{{$.inputs.exclude_cancelled_absences}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_absences)}}' + response: + collection: true + result: + data: $.steps.get_employee_absences.output.data + - actionId: list_employee_benefits + categories: + - hris + actionType: get + label: Get Employee Benefits + description: Get benefits for a specific employee by ID + details: "Returns a paginated list of benefits for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns benefit data including benefit types, coverage details, and other benefit properties." + inputs: + - name: id + description: The ID of the employee whose benefits to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_employee_benefits + description: Get employee benefits request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/benefits + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_employee_benefits.output.data + - actionId: list_employee_bonuses + categories: + - hris + actionType: get + label: Get Employee Bonuses + description: Get bonuses for a specific employee by ID + details: "Returns a paginated list of bonuses for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns bonus data including amounts, dates, reasons, and other bonus properties." + inputs: + - name: id + description: The ID of the employee whose bonuses to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_employee_bonuses + description: Get employee bonuses request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/bonuses + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_employee_bonuses.output.data + - actionId: list_employee_change_requests + categories: + - hris + actionType: get + label: Get Employee Change Requests + description: Get change requests for a specific employee by ID + details: "Returns a paginated list of change requests for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns change request data including request types, status, dates, and other change request properties." + inputs: + - name: id + description: The ID of the employee whose change requests to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_employee_change_requests + description: Get employee change requests request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/change_requests + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_employee_change_requests.output.data + - actionId: list_employee_holiday_years + categories: + - hris + actionType: get + label: Get Employee Holiday Years + description: Get holiday years for a specific employee by ID + details: "Returns a list of holiday years for a specific employee. Requires employee ID. Supports optional filtering by date (for_date) to return holiday years that include a specific date. Returns holiday year data including year, allowance, taken, remaining, and other holiday year properties." + inputs: + - name: id + description: ID of employee to return holiday years for. + type: string + in: path + required: true + - name: for_date + description: Return employee_holiday_years that include this date. + type: string + in: query + required: false + steps: + - stepId: get_employee_holiday_years + description: Get employee holiday years request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/employee_holiday_years + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: for_date + value: '{{$.inputs.for_date}}' + in: query + condition: '{{present($.inputs.for_date)}}' + response: + collection: true + result: + data: $.steps.get_employee_holiday_years.output.data + - actionId: list_employee_leave_requests + categories: + - hris + actionType: get + label: Get Employee Leave Requests + description: Get leave requests for a specific employee by ID + details: "Returns a list of leave requests for a specific employee. Requires employee ID. Supports optional filtering to exclude cancelled requests. Returns leave request data including dates, type, status, and other leave request properties." + inputs: + - name: id + description: ID of employee to return leave requests for. + type: string + in: path + required: true + - name: exclude_cancelled_requests + description: Don't return cancelled leave requests. + type: boolean + in: query + required: false + steps: + - stepId: get_employee_leave_requests + description: Get employee leave requests request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/leave_requests + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: exclude_cancelled_requests + value: '{{$.inputs.exclude_cancelled_requests}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_requests)}}' + response: + collection: true + result: + data: $.steps.get_employee_leave_requests.output.data + - actionId: list_employee_salaries + categories: + - hris + actionType: get + label: Get Employee Salaries + description: Get salaries for a specific employee by ID + details: "Returns a paginated list of salaries for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns salary data including amounts, effective dates, and other salary properties." + inputs: + - name: id + description: The ID of the employee whose salaries to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_employee_salaries + description: Get employee salaries request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/salaries + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_employee_salaries.output.data + - actionId: list_employee_sicknesses + categories: + - hris + actionType: get + label: Get Employee Sicknesses + description: Get sicknesses for a specific employee by ID + details: "Returns a list of sickness records for a specific employee. Requires employee ID. Returns sickness data including dates, type, status, reason, and other sickness properties." + inputs: + - name: id + description: ID of employee to return sicknesses for. + type: string + in: path + required: true + steps: + - stepId: get_employee_sicknesses + description: Get employee sicknesses request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/sicknesses + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_employee_sicknesses.output.data + - actionId: create_employee_leave_request + categories: + - hris + actionType: create + label: Create Employee Leave Request + description: Create a leave request for a specific employee + details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." + inputs: + - name: id + description: ID of employee to create leave request for. + type: number + in: path + required: true + - name: exclude_cancelled_requests + description: Don't return cancelled leave requests. + type: boolean + in: body + required: false + - name: leave_request[start_date] + description: Start date of the leave request. + type: string + in: body + required: true + - name: leave_request[end_date] + description: End date of the leave request. + type: string + in: body + required: true + - name: leave_request[half_start] + description: Whether the start date is a half day. + type: boolean + in: body + required: true + - name: leave_request[half_start_am_pm] + description: Whether the half start day is AM or PM. + type: string + in: body + required: false + - name: leave_request[half_end] + description: Whether the end date is a half day. + type: boolean + in: body + required: false + - name: leave_request[half_end_am_pm] + description: Whether the half end day is AM or PM. + type: string + in: body + required: false + - name: leave_request[type] + description: Type of leave request. + type: string + in: body + required: false + - name: leave_request[notes] + description: Notes for the leave request. + type: string + in: body + required: false + steps: + - stepId: create_leave_request + description: Create employee leave request + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/leave_requests + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: exclude_cancelled_requests + value: '{{$.inputs.exclude_cancelled_requests}}' + in: body + condition: '{{present($.inputs.exclude_cancelled_requests)}}' + - name: leave_request[start_date] + value: "{{$.inputs['leave_request[start_date]']}}" + in: body + - name: leave_request[end_date] + value: "{{$.inputs['leave_request[end_date]']}}" + in: body + - name: leave_request[half_start] + value: "{{$.inputs['leave_request[half_start]']}}" + in: body + - name: leave_request[half_start_am_pm] + value: "{{$.inputs['leave_request[half_start_am_pm]']}}" + in: body + condition: "{{present($.inputs['leave_request[half_start_am_pm]'])}}" + - name: leave_request[half_end] + value: "{{$.inputs['leave_request[half_end]']}}" + in: body + condition: "{{present($.inputs['leave_request[half_end]'])}}" + - name: leave_request[half_end_am_pm] + value: "{{$.inputs['leave_request[half_end_am_pm]']}}" + in: body + condition: "{{present($.inputs['leave_request[half_end_am_pm]'])}}" + - name: leave_request[type] + value: "{{$.inputs['leave_request[type]']}}" + in: body + condition: "{{present($.inputs['leave_request[type]'])}}" + - name: leave_request[notes] + value: "{{$.inputs['leave_request[notes]']}}" + in: body + condition: "{{present($.inputs['leave_request[notes]'])}}" + response: + collection: false + result: + data: $.steps.create_leave_request.output.data + - actionId: create_employee_sickness + categories: + - hris + actionType: create + label: Create Employee Sickness + description: Create a sickness record for a specific employee + details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." + inputs: + - name: id + description: Employee id. + type: number + in: path + required: true + - name: sickness[company_sicknesstype_id] + description: Company sickness type ID. + type: number + in: body + required: true + - name: sickness[start_date] + description: Start date of the sickness. + type: string + in: body + required: true + - name: sickness[half_start] + description: Whether the start date is a half day. + type: boolean + in: body + required: false + - name: sickness[half_start_am_pm] + description: Whether the half start day is AM or PM. + type: string + in: body + required: false + - name: sickness[half_end] + description: Whether the end date is a half day. + type: boolean + in: body + required: false + - name: sickness[half_end_am_pm] + description: Whether the half end day is AM or PM. + type: string + in: body + required: false + - name: sickness[half_day] + description: Whether it is a half day sickness. + type: boolean + in: body + required: false + - name: sickness[status] + description: Status of the sickness. + type: string + in: body + required: false + - name: sickness[reason] + description: Reason for the sickness. + type: string + in: body + required: false + - name: sickness[end_date] + description: End date of the sickness. + type: string + in: body + required: false + - name: sickness[review_notes] + description: Review notes for the sickness. + type: string + in: body + required: false + steps: + - stepId: create_sickness + description: Create employee sickness + stepFunction: + functionName: request + parameters: + url: /employees/${inputs.id}/sicknesses + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: sickness[company_sicknesstype_id] + value: "{{$.inputs['sickness[company_sicknesstype_id]']}}" + in: body + - name: sickness[start_date] + value: "{{$.inputs['sickness[start_date]']}}" + in: body + - name: sickness[half_start] + value: "{{$.inputs['sickness[half_start]']}}" + in: body + condition: "{{present($.inputs['sickness[half_start]'])}}" + - name: sickness[half_start_am_pm] + value: "{{$.inputs['sickness[half_start_am_pm]']}}" + in: body + condition: "{{present($.inputs['sickness[half_start_am_pm]'])}}" + - name: sickness[half_end] + value: "{{$.inputs['sickness[half_end]']}}" + in: body + condition: "{{present($.inputs['sickness[half_end]'])}}" + - name: sickness[half_end_am_pm] + value: "{{$.inputs['sickness[half_end_am_pm]']}}" + in: body + condition: "{{present($.inputs['sickness[half_end_am_pm]'])}}" + - name: sickness[half_day] + value: "{{$.inputs['sickness[half_day]']}}" + in: body + condition: "{{present($.inputs['sickness[half_day]'])}}" + - name: sickness[status] + value: "{{$.inputs['sickness[status]']}}" + in: body + condition: "{{present($.inputs['sickness[status]'])}}" + - name: sickness[reason] + value: "{{$.inputs['sickness[reason]']}}" + in: body + condition: "{{present($.inputs['sickness[reason]'])}}" + - name: sickness[end_date] + value: "{{$.inputs['sickness[end_date]']}}" + in: body + condition: "{{present($.inputs['sickness[end_date]'])}}" + - name: sickness[review_notes] + value: "{{$.inputs['sickness[review_notes]']}}" + in: body + condition: "{{present($.inputs['sickness[review_notes]'])}}" + response: + collection: false + result: + data: $.steps.create_sickness.output.data + # Blind Mapped as the provider gives 500 while creating with right payload (need to recheck) + - actionId: create_employee + categories: + - hris + actionType: create + schema: employees + label: Create Employee + description: Create a new employee + details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." + inputs: + - name: employee[first_name] + description: First name. + type: string + in: body + required: true + - name: employee[last_name] + description: Last name. + type: string + in: body + required: true + - name: employee[email] + description: Work email. + type: string + in: body + required: true + - name: employee[company_join_date] + description: Company join date. + type: string + in: body + required: true + - name: employee[middle_name] + description: Middle name. + type: string + in: body + required: false + - name: employee[gender] + description: Gender id. + type: string + in: body + required: false + - name: employee[dob] + description: Date of birth. + type: string + in: body + required: false + - name: employee[marital_status] + description: Marital status. + type: string + in: body + required: false + - name: employee[nationality] + description: Nationality. + type: string + in: body + required: false + - name: employee[national_insurance_no] + description: National Insurance Number. + type: string + in: body + required: false + - name: employee[ethnicity] + description: Ethnicity. + type: string + in: body + required: false + - name: employee[ethnicity_id] + description: Ethnicity id. + type: string + in: body + required: false + - name: employee[ddi] + description: Direct dial-in number. + type: string + in: body + required: false + - name: employee[work_ext] + description: Work extension number. + type: string + in: body + required: false + - name: employee[work_mobile] + description: Work mobile number. + type: string + in: body + required: false + - name: employee[personal_mobile] + description: Personal mobile number. + type: string + in: body + required: false + - name: employee[home_telephone] + description: Home telephone number. + type: string + in: body + required: false + - name: employee[status] + description: Employee status. + type: string + in: body + required: false + - name: employee[job_title] + description: Job title. + type: string + in: body + required: false + - name: employee[full_or_part_time] + description: Fulltime or parttime employee. + type: string + in: body + required: false + - name: employee[job_start_date] + description: Job start date. + type: string + in: body + required: false + - name: employee[employee_ref] + description: Employee reference number. + type: string + in: body + required: false + - name: employee[person_type] + description: Person type. + type: string + in: body + required: false + - name: employee[department] + description: Department id. + type: number + in: body + required: false + - name: employee[division] + description: Division id. + type: number + in: body + required: false + - name: employee[location] + description: Location id. + type: number + in: body + required: false + - name: employee[working_pattern] + description: Working pattern id. + type: number + in: body + required: false + - name: employee[working_pattern_id] + description: Working pattern id. + type: number + in: body + required: false + - name: employee[holiday_allowance] + description: Holiday Allowance id. + type: number + in: body + required: false + - name: employee[holiday_allowance_id] + description: Holiday Allowance id. + type: number + in: body + required: false + - name: employee[receives_statutory_holidays] + description: Employee receives statutory holidays for their country. + type: boolean + in: body + required: false + - name: employee[statutory_holiday_country] + description: Statutory holiday country name. + type: string + in: body + required: false + - name: employee[statutory_holiday_country_id] + description: Statutory holiday country id. + type: number + in: body + required: false + - name: employee[ignore_working_pattern] + description: Ignore the working pattern and allow manual entry of time lost when requesting leave. + type: boolean + in: body + required: false + - name: employee[hr] + description: Employee is an HR manager. + type: boolean + in: body + required: false + - name: employee[salary][amount] + description: Salary amount. + type: number + in: body + required: false + - name: employee[salary][basis] + description: Salary basis. + type: string + in: body + required: false + - name: employee[salary][start_date] + description: Salary start date. + type: string + in: body + required: false + - name: employee[salary][payment_schedule] + description: Salary payment schedule. + type: string + in: body + required: false + steps: + - stepId: create_new_employee + description: Create new employee + stepFunction: + functionName: request + parameters: + url: /employees + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: employee[first_name] + value: "{{$.inputs['employee[first_name]']}}" + in: body + - name: employee[last_name] + value: "{{$.inputs['employee[last_name]']}}" + in: body + - name: employee[email] + value: "{{$.inputs['employee[email]']}}" + in: body + - name: employee[company_join_date] + value: "{{$.inputs['employee[company_join_date]']}}" + in: body + - name: employee[middle_name] + value: "{{$.inputs['employee[middle_name]']}}" + in: body + condition: "{{present($.inputs['employee[middle_name]'])}}" + - name: employee[gender] + value: "{{$.inputs['employee[gender]']}}" + in: body + condition: "{{present($.inputs['employee[gender]'])}}" + - name: employee[dob] + value: "{{$.inputs['employee[dob]']}}" + in: body + condition: "{{present($.inputs['employee[dob]'])}}" + - name: employee[marital_status] + value: "{{$.inputs['employee[marital_status]']}}" + in: body + condition: "{{present($.inputs['employee[marital_status]'])}}" + - name: employee[nationality] + value: "{{$.inputs['employee[nationality]']}}" + in: body + condition: "{{present($.inputs['employee[nationality]'])}}" + - name: employee[national_insurance_no] + value: "{{$.inputs['employee[national_insurance_no]']}}" + in: body + condition: "{{present($.inputs['employee[national_insurance_no]'])}}" + - name: employee[ethnicity] + value: "{{$.inputs['employee[ethnicity]']}}" + in: body + condition: "{{present($.inputs['employee[ethnicity]'])}}" + - name: employee[ethnicity_id] + value: "{{$.inputs['employee[ethnicity_id]']}}" + in: body + condition: "{{present($.inputs['employee[ethnicity_id]'])}}" + - name: employee[ddi] + value: "{{$.inputs['employee[ddi]']}}" + in: body + condition: "{{present($.inputs['employee[ddi]'])}}" + - name: employee[work_ext] + value: "{{$.inputs['employee[work_ext]']}}" + in: body + condition: "{{present($.inputs['employee[work_ext]'])}}" + - name: employee[work_mobile] + value: "{{$.inputs['employee[work_mobile]']}}" + in: body + condition: "{{present($.inputs['employee[work_mobile]'])}}" + - name: employee[personal_mobile] + value: "{{$.inputs['employee[personal_mobile]']}}" + in: body + condition: "{{present($.inputs['employee[personal_mobile]'])}}" + - name: employee[home_telephone] + value: "{{$.inputs['employee[home_telephone]']}}" + in: body + condition: "{{present($.inputs['employee[home_telephone]'])}}" + - name: employee[status] + value: "{{$.inputs['employee[status]']}}" + in: body + condition: "{{present($.inputs['employee[status]'])}}" + - name: employee[job_title] + value: "{{$.inputs['employee[job_title]']}}" + in: body + condition: "{{present($.inputs['employee[job_title]'])}}" + - name: employee[full_or_part_time] + value: "{{$.inputs['employee[full_or_part_time]']}}" + in: body + condition: "{{present($.inputs['employee[full_or_part_time]'])}}" + - name: employee[job_start_date] + value: "{{$.inputs['employee[job_start_date]']}}" + in: body + condition: "{{present($.inputs['employee[job_start_date]'])}}" + - name: employee[employee_ref] + value: "{{$.inputs['employee[employee_ref]']}}" + in: body + condition: "{{present($.inputs['employee[employee_ref]'])}}" + - name: employee[person_type] + value: "{{$.inputs['employee[person_type]']}}" + in: body + condition: "{{present($.inputs['employee[person_type]'])}}" + - name: employee[department] + value: "{{$.inputs['employee[department]']}}" + in: body + condition: "{{present($.inputs['employee[department]'])}}" + - name: employee[division] + value: "{{$.inputs['employee[division]']}}" + in: body + condition: "{{present($.inputs['employee[division]'])}}" + - name: employee[location] + value: "{{$.inputs['employee[location]']}}" + in: body + condition: "{{present($.inputs['employee[location]'])}}" + - name: employee[working_pattern] + value: "{{$.inputs['employee[working_pattern]']}}" + in: body + condition: "{{present($.inputs['employee[working_pattern]'])}}" + - name: employee[working_pattern_id] + value: "{{$.inputs['employee[working_pattern_id]']}}" + in: body + condition: "{{present($.inputs['employee[working_pattern_id]'])}}" + - name: employee[holiday_allowance] + value: "{{$.inputs['employee[holiday_allowance]']}}" + in: body + condition: "{{present($.inputs['employee[holiday_allowance]'])}}" + - name: employee[holiday_allowance_id] + value: "{{$.inputs['employee[holiday_allowance_id]']}}" + in: body + condition: "{{present($.inputs['employee[holiday_allowance_id]'])}}" + - name: employee[receives_statutory_holidays] + value: "{{$.inputs['employee[receives_statutory_holidays]']}}" + in: body + condition: "{{present($.inputs['employee[receives_statutory_holidays]'])}}" + - name: employee[statutory_holiday_country] + value: "{{$.inputs['employee[statutory_holiday_country]']}}" + in: body + condition: "{{present($.inputs['employee[statutory_holiday_country]'])}}" + - name: employee[statutory_holiday_country_id] + value: "{{$.inputs['employee[statutory_holiday_country_id]']}}" + in: body + condition: "{{present($.inputs['employee[statutory_holiday_country_id]'])}}" + - name: employee[ignore_working_pattern] + value: "{{$.inputs['employee[ignore_working_pattern]']}}" + in: body + condition: "{{present($.inputs['employee[ignore_working_pattern]'])}}" + - name: employee[hr] + value: "{{$.inputs['employee[hr]']}}" + in: body + condition: "{{present($.inputs['employee[hr]'])}}" + - name: employee[salary][amount] + value: "{{$.inputs['employee[salary][amount]']}}" + in: body + condition: "{{present($.inputs['employee[salary][amount]'])}}" + - name: employee[salary][basis] + value: "{{$.inputs['employee[salary][basis]']}}" + in: body + condition: "{{present($.inputs['employee[salary][basis]'])}}" + - name: employee[salary][start_date] + value: "{{$.inputs['employee[salary][start_date]']}}" + in: body + condition: "{{present($.inputs['employee[salary][start_date]'])}}" + - name: employee[salary][payment_schedule] + value: "{{$.inputs['employee[salary][payment_schedule]']}}" + in: body + condition: "{{present($.inputs['employee[salary][payment_schedule]'])}}" + response: + collection: false + result: + data: $.steps.create_new_employee.output.data + - actionId: list_holiday_allowances + categories: + - hris + actionType: list + label: List Holiday Allowances + description: Get a list of holiday allowances + details: "Returns a paginated list of all holiday allowances in the BreatheHR system. Supports pagination with page and per_page. Returns holiday allowance data including ID, employee_id, allowance_type, days, and other allowance properties." + steps: + - stepId: get_all_holiday_allowances + description: Get holiday allowances request + stepFunction: + functionName: request + parameters: + url: /holiday_allowances + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_all_holiday_allowances.output.data + - actionId: list_statutory_holiday_countries + categories: + - hris + actionType: list + label: List Statutory Holiday Countries + description: Get a list of statutory holiday countries + details: "Returns a list of all statutory holiday countries configured in BreatheHR. Returns country data including country code, name, and other country properties." + steps: + - stepId: get_statutory_holiday_countries + description: Get statutory holiday countries request + stepFunction: + functionName: request + parameters: + url: /statutory_holiday_countries + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_statutory_holiday_countries.output.data + - actionId: list_leave_requests + categories: + - hris + actionType: list + label: List Leave Requests + description: Get a list of leave requests + details: "Returns a paginated list of all leave requests in the BreatheHR system. Supports filtering by employee_id, department_id, status, start_date, end_date, and pagination with page and per_page. Returns leave request data including ID, employee_id, type, start_date, end_date, status, and other leave request properties." + inputs: + - name: start_date + description: Return leave requests starting on or after this date. + type: string + in: query + required: false + - name: end_date + description: Return leave requests starting on or before this date. + type: string + in: query + required: false + - name: exclude_cancelled_requests + description: Don't return cancelled leave requests. + type: boolean + in: query + required: false + - name: employee_id + description: Return leave requests for this employee only. + type: number + in: query + required: false + - name: department_id + description: Return leave requests for this department only. + type: number + in: query + required: false + - name: rotacloud + description: Return leave requests where rotacloud is not excluded from integration. + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_leave_requests + description: Get leave requests request + stepFunction: + functionName: request + parameters: + url: /leave_requests + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: start_date + value: '{{$.inputs.start_date}}' + in: query + condition: '{{present($.inputs.start_date)}}' + - name: end_date + value: '{{$.inputs.end_date}}' + in: query + condition: '{{present($.inputs.end_date)}}' + - name: exclude_cancelled_requests + value: '{{$.inputs.exclude_cancelled_requests}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_requests)}}' + - name: employee_id + value: '{{$.inputs.employee_id}}' + in: query + condition: '{{present($.inputs.employee_id)}}' + - name: department_id + value: '{{$.inputs.department_id}}' + in: query + condition: '{{present($.inputs.department_id)}}' + - name: rotacloud + value: '{{$.inputs.rotacloud}}' + in: query + condition: '{{present($.inputs.rotacloud)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_leave_requests.output.data + - actionId: get_leave_request + categories: + - hris + actionType: get + label: Get Leave Request + description: Get a specific leave request by ID + details: "Returns detailed information for a specific leave request by its ID. Includes leave request ID, employee_id, type, start_date, end_date, status, and all other leave request properties." + inputs: + - name: id + description: The ID of the leave request to retrieve. + type: string + in: path + required: true + steps: + - stepId: get_a_leave_request + description: Get leave request request + stepFunction: + functionName: request + parameters: + url: /leave_requests/${inputs.id} + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.get_a_leave_request.output.data + - actionId: get_leave_request_cancelling + categories: + - hris + actionType: get + label: Get Leave Request Cancelling + description: Find the request this leave request is cancelling + details: "Returns detailed information for a specific leave request by its ID. Includes leave request ID, employee_id, type, start_date, end_date, status, and all other leave request properties." + inputs: + - name: id + description: The ID of the leave request. + type: string + in: path + required: true + steps: + - stepId: get_leave_request_cancelling_step + description: Get leave request cancelling request + stepFunction: + functionName: request + parameters: + url: /leave_requests/${inputs.id}/cancelling + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.get_leave_request_cancelling_step.output.data + - actionId: list_other_leave_reasons + categories: + - hris + actionType: list + label: List Other Leave Reasons + description: Get a list of other leave reasons + details: "Returns a list of all other leave reasons configured in BreatheHR. Returns leave reason data including ID, name, and other leave reason properties." + steps: + - stepId: get_other_leave_reasons + description: Get other leave reasons request + stepFunction: + functionName: request + parameters: + url: /leave_requests/other_leave_reasons + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_other_leave_reasons.output.data + - actionId: list_other_leave_reasons_main + categories: + - hris + actionType: list + label: List Other Leave Reasons + description: Get a list of other leave reasons + details: "Returns a list of all other leave reasons configured in BreatheHR. Returns leave reason data including ID, name, and other leave reason properties." + steps: + - stepId: get_all_other_leave_reasons + description: Get other leave reasons request + stepFunction: + functionName: request + parameters: + url: /other_leave_reasons + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_all_other_leave_reasons_v1.output.data + - actionId: approve_leave_request + categories: + - hris + actionType: update + label: Approve Leave Request + description: Approve a specific leave request by ID + details: "Approves a leave request in BreatheHR. Requires leave request ID. Updates the leave request status to approved. Returns the updated leave request with approval status." + inputs: + - name: id + description: The ID of the leave request to approve. + type: string + in: path + required: true + steps: + - stepId: approve_leave_request + description: Approve leave request + stepFunction: + functionName: request + parameters: + url: /leave_requests/${inputs.id}/approve + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.approve_leave_request.output.data + - actionId: reject_leave_request + categories: + - hris + actionType: update + label: Reject Leave Request + description: Reject a specific leave request by ID + details: "Rejects a leave request in BreatheHR. Requires leave request ID and rejection_reason. Updates the leave request status to rejected. Returns the updated leave request with rejection status and reason." + inputs: + - name: id + description: The ID of the leave request to reject. + type: string + in: path + required: true + - name: leave_request[rejection_reason] + description: Rejection reason. + type: string + in: body + required: true + steps: + - stepId: reject_leave_request + description: Reject leave request + stepFunction: + functionName: request + parameters: + url: /leave_requests/${inputs.id}/reject + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: leave_request[rejection_reason] + value: "{{$.inputs['leave_request[rejection_reason]']}}" + in: body + response: + collection: false + result: + data: $.steps.reject_leave_request.output.data + - actionId: list_locations + categories: + - hris + actionType: list + label: List Locations + description: Get a list of locations + details: "Returns a paginated list of all locations in the BreatheHR system. Supports pagination with page and per_page. Returns location data including ID, name, address, and other location properties." + steps: + - stepId: get_all_locations + description: Get locations request + stepFunction: + functionName: request + parameters: + url: /locations + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_all_locations.output.data + - actionId: list_salaries + categories: + - hris + actionType: list + label: List Salaries + description: Get a list of salaries + details: "Returns a paginated list of all salaries in the BreatheHR system. Supports pagination with page and per_page. Returns salary data including ID, employee_id, amount, currency, frequency, and other salary properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_salaries + description: Get salaries request + stepFunction: + functionName: request + parameters: + url: /salaries + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_salaries.output.data + - actionId: list_sicknesses + categories: + - hris + actionType: list + label: List Sicknesses + description: Get a list of sicknesses + details: "Returns a paginated list of all sickness records in the BreatheHR system. Supports filtering by employee_id, department_id, start_date, end_date, and pagination with page and per_page. Returns sickness data including ID, employee_id, start_date, end_date, status, and other sickness properties." + inputs: + - name: start_date + description: Return sicknesses starting on or after this date. + type: string + in: query + required: false + - name: end_date + description: Return sicknesses starting on or before this date. + type: string + in: query + required: false + - name: employee_id + description: Return sicknesses for this employee only. + type: number + in: query + required: false + - name: department_id + description: Return sicknesses for this department only. + type: number + in: query + required: false + - name: rotacloud + description: Return sicknesses for employees without exclude from rotacloud. + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_sicknesses + description: Get sicknesses request + stepFunction: + functionName: request + parameters: + url: /sicknesses + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: start_date + value: '{{$.inputs.start_date}}' + in: query + condition: '{{present($.inputs.start_date)}}' + - name: end_date + value: '{{$.inputs.end_date}}' + in: query + condition: '{{present($.inputs.end_date)}}' + - name: employee_id + value: '{{$.inputs.employee_id}}' + in: query + condition: '{{present($.inputs.employee_id)}}' + - name: department_id + value: '{{$.inputs.department_id}}' + in: query + condition: '{{present($.inputs.department_id)}}' + - name: rotacloud + value: '{{$.inputs.rotacloud}}' + in: query + condition: '{{present($.inputs.rotacloud)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_sicknesses.output.data + - actionId: update_sickness + categories: + - hris + actionType: update + label: Update Sickness + description: Update a specific sickness record by ID + details: "Updates an existing sickness record. Requires sickness ID. Supports updating start_date, end_date, status, and other sickness properties. Returns the updated sickness record with all properties." + inputs: + - name: id + description: Sickness ID. + type: number + in: path + required: true + - name: sickness[company_sicknesstype_id] + description: Company sickness type ID. + type: number + in: body + required: false + - name: sickness[start_date] + description: Start date of the sickness. + type: string + in: body + required: false + - name: sickness[half_start] + description: Whether the start date is a half day. + type: boolean + in: body + required: false + - name: sickness[half_start_am_pm] + description: Whether the half start day is AM or PM. + type: string + in: body + required: false + - name: sickness[half_end] + description: Whether the end date is a half day. + type: boolean + in: body + required: false + - name: sickness[half_end_am_pm] + description: Whether the half end day is AM or PM. + type: string + in: body + required: false + - name: sickness[half_day] + description: Whether it is a half day sickness. + type: boolean + in: body + required: false + - name: sickness[status] + description: Status of the sickness. + type: string + in: body + required: false + - name: sickness[reason] + description: Reason for the sickness. + type: string + in: body + required: false + - name: sickness[end_date] + description: End date of the sickness. + type: string + in: body + required: false + - name: sickness[review_notes] + description: Review notes for the sickness. + type: string + in: body + required: false + steps: + - stepId: update_sickness_record + description: Update sickness record + stepFunction: + functionName: request + parameters: + url: /sicknesses/${inputs.id} + method: put + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: sickness[company_sicknesstype_id] + value: "{{$.inputs['sickness[company_sicknesstype_id]']}}" + in: body + condition: "{{present($.inputs['sickness[company_sicknesstype_id]'])}}" + - name: sickness[start_date] + value: "{{$.inputs['sickness[start_date]']}}" + in: body + condition: "{{present($.inputs['sickness[start_date]'])}}" + - name: sickness[half_start] + value: "{{$.inputs['sickness[half_start]']}}" + in: body + condition: "{{present($.inputs['sickness[half_start]'])}}" + - name: sickness[half_start_am_pm] + value: "{{$.inputs['sickness[half_start_am_pm]']}}" + in: body + condition: "{{present($.inputs['sickness[half_start_am_pm]'])}}" + - name: sickness[half_end] + value: "{{$.inputs['sickness[half_end]']}}" + in: body + condition: "{{present($.inputs['sickness[half_end]'])}}" + - name: sickness[half_end_am_pm] + value: "{{$.inputs['sickness[half_end_am_pm]']}}" + in: body + condition: "{{present($.inputs['sickness[half_end_am_pm]'])}}" + - name: sickness[half_day] + value: "{{$.inputs['sickness[half_day]']}}" + in: body + condition: "{{present($.inputs['sickness[half_day]'])}}" + - name: sickness[status] + value: "{{$.inputs['sickness[status]']}}" + in: body + condition: "{{present($.inputs['sickness[status]'])}}" + - name: sickness[reason] + value: "{{$.inputs['sickness[reason]']}}" + in: body + condition: "{{present($.inputs['sickness[reason]'])}}" + - name: sickness[end_date] + value: "{{$.inputs['sickness[end_date]']}}" + in: body + condition: "{{present($.inputs['sickness[end_date]'])}}" + - name: sickness[review_notes] + value: "{{$.inputs['sickness[review_notes]']}}" + in: body + condition: "{{present($.inputs['sickness[review_notes]'])}}" + response: + collection: false + result: + data: $.steps.update_sickness_record.output.data + - actionId: delete_sickness + categories: + - hris + actionType: delete + label: Delete Sickness + description: Delete a specific sickness record by ID + details: "Deletes a sickness record from BreatheHR. Requires sickness ID. This action permanently removes the sickness record. Returns a success indicator." + inputs: + - name: id + description: Sickness ID. + type: number + in: path + required: true + steps: + - stepId: delete_sickness_record + description: Delete sickness record + stepFunction: + functionName: request + parameters: + url: /sicknesses/${inputs.id} + method: delete + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.delete_sickness_record.output.data + - actionId: list_working_patterns + categories: + - hris + actionType: list + label: List Working Patterns + description: Get a list of working patterns + details: "Returns a paginated list of all working patterns in the BreatheHR system. Supports pagination with page and per_page. Returns working pattern data including ID, name, type, hours, and other working pattern properties." + steps: + - stepId: get_all_working_patterns + description: Get working patterns request + stepFunction: + functionName: request + parameters: + url: /working_patterns + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_all_working_patterns.output.data + - actionId: list_absences + categories: + - hris + actionType: list + label: List Absences + description: Get a list of absences + details: "Returns a paginated list of all absences in the BreatheHR system. Supports filtering by type (Holiday or OtherLeave), other_leave_reason_id, exclude_cancelled_absences flag, employee_id, department_id, start_date, end_date, rotacloud flag, and pagination with page and per_page. Returns absence data including ID, employee_id, type, start_date, end_date, status, and other absence properties." + inputs: + - name: type + description: Type of absences to return, 'Holiday' or 'OtherLeave'. + type: string + in: query + required: false + - name: other_leave_reason_id + description: ID of other leave reason to filter by. + type: number + in: query + required: false + - name: exclude_cancelled_absences + description: Don't return cancelled absences. + type: boolean + in: query + required: false + - name: employee_id + description: Return absences for this employee only. + type: number + in: query + required: false + - name: department_id + description: Return absences for this department only. + type: number + in: query + required: false + - name: start_date + description: Return absences on or after this date. + type: string + in: query + required: false + - name: end_date + description: Return absences on or before this date. + type: string + in: query + required: false + - name: rotacloud + description: Return absences where rotacloud is not excluded from integration. + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_absences + description: Get absences request + stepFunction: + functionName: request + parameters: + url: /absences + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: type + value: '{{$.inputs.type}}' + in: query + condition: '{{present($.inputs.type)}}' + - name: other_leave_reason_id + value: '{{$.inputs.other_leave_reason_id}}' + in: query + condition: '{{present($.inputs.other_leave_reason_id)}}' + - name: exclude_cancelled_absences + value: '{{$.inputs.exclude_cancelled_absences}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_absences)}}' + - name: employee_id + value: '{{$.inputs.employee_id}}' + in: query + condition: '{{present($.inputs.employee_id)}}' + - name: department_id + value: '{{$.inputs.department_id}}' + in: query + condition: '{{present($.inputs.department_id)}}' + - name: start_date + value: '{{$.inputs.start_date}}' + in: query + condition: '{{present($.inputs.start_date)}}' + - name: end_date + value: '{{$.inputs.end_date}}' + in: query + condition: '{{present($.inputs.end_date)}}' + - name: rotacloud + value: '{{$.inputs.rotacloud}}' + in: query + condition: '{{present($.inputs.rotacloud)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_absences.output.data + - actionId: cancel_absence + categories: + - hris + actionType: update + label: Cancel Absence + description: Cancel a specific absence by ID + details: "Cancels an existing absence. Requires absence ID. This action marks the absence as cancelled. Returns a success indicator." + inputs: + - name: id + description: ID of absence to cancel. + type: number + in: path + required: true + - name: reason + description: Reason for cancellation. + type: string + in: body + required: false + steps: + - stepId: cancel_absence_request + description: Cancel absence request + stepFunction: + functionName: request + parameters: + url: /absences/${inputs.id}/cancel + method: post + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: Content-Type + value: application/x-www-form-urlencoded + in: headers + - name: reason + value: '{{$.inputs.reason}}' + in: body + condition: '{{present($.inputs.reason)}}' + response: + collection: false + result: + data: $.steps.cancel_absence_request.output.data + - actionId: get_account + categories: + - hris + actionType: get + label: Get Account Details + description: Get account details + details: "Returns account details for the BreatheHR account. Includes account information such as company name, settings, and other account properties." + steps: + - stepId: get_account_details + description: Get account details request + stepFunction: + functionName: request + parameters: + url: /account + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: false + result: + data: $.steps.get_account_details.output.data + - actionId: list_benefits + categories: + - hris + actionType: list + label: List Benefits + description: Get a list of benefits + details: "Returns a paginated list of all benefits in the BreatheHR system. Supports pagination with page and per_page. Returns benefit data including ID, name, type, and other benefit properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_benefits + description: Get benefits request + stepFunction: + functionName: request + parameters: + url: /benefits + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_benefits.output.data + - actionId: list_bonuses + categories: + - hris + actionType: list + label: List Bonuses + description: Get a list of bonuses + details: "Returns a paginated list of all bonuses in the BreatheHR system. Supports pagination with page and per_page. Returns bonus data including ID, employee_id, type, amount, date, and other bonus properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_bonuses + description: Get bonuses request + stepFunction: + functionName: request + parameters: + url: /bonuses + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_bonuses.output.data + - actionId: list_company_documents + categories: + - hris + actionType: list + label: List Company Documents + description: Get a list of company documents + details: "Returns a paginated list of all company documents in the BreatheHR system. Supports pagination with page and per_page. Returns document data including ID, name, type, and other document properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_company_documents + description: Get company documents request + stepFunction: + functionName: request + parameters: + url: /company_documents + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_company_documents.output.data + - actionId: list_departments + categories: + - hris + actionType: list + label: List Departments + description: Get a list of departments + details: "Returns a paginated list of all departments in the BreatheHR system. Supports pagination with page and per_page. Returns department data including ID, name, and other department properties." + inputs: + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_departments + description: Get departments request + stepFunction: + functionName: request + parameters: + url: /departments + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_departments.output.data + - actionId: list_department_absences + categories: + - hris + actionType: get + label: Get Department Absences + description: Get absences for a specific department by ID + details: "Returns a paginated list of absences for a specific department. Requires department ID. Supports optional filtering to exclude cancelled absences and pagination with page and per_page parameters. Returns absence data including dates, reasons, and other absence properties." + inputs: + - name: id + description: ID of department to return absences for. + type: string + in: path + required: true + - name: exclude_cancelled_absences + description: Don't return cancelled leave requests. + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_department_absences + description: Get department absences request + stepFunction: + functionName: request + parameters: + url: /departments/${inputs.id}/absences + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: exclude_cancelled_absences + value: '{{$.inputs.exclude_cancelled_absences}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_absences)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_department_absences.output.data + - actionId: list_department_benefits + categories: + - hris + actionType: get + label: Get Department Benefits + description: Get benefits for a specific department by ID + details: "Returns a paginated list of benefits for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns benefit data including benefit types, coverage details, and other benefit properties." + inputs: + - name: id + description: The ID of the department whose benefits to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_department_benefits + description: Get department benefits request + stepFunction: + functionName: request + parameters: + url: /departments/${inputs.id}/benefits + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_department_benefits.output.data + - actionId: list_department_bonuses + categories: + - hris + actionType: get + label: Get Department Bonuses + description: Get bonuses for a specific department by ID + details: "Returns a paginated list of bonuses for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns bonus data including amounts, dates, reasons, and other bonus properties." + inputs: + - name: id + description: The ID of the department whose bonuses to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_department_bonuses + description: Get department bonuses request + stepFunction: + functionName: request + parameters: + url: /departments/${inputs.id}/bonuses + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_department_bonuses.output.data + - actionId: list_department_leave_requests + categories: + - hris + actionType: get + label: Get Department Leave Requests + description: Get leave requests for a specific department by ID + details: "Returns a paginated list of leave requests for a specific department. Requires department ID. Supports optional filtering to exclude cancelled requests and pagination with page and per_page parameters. Returns leave request data including dates, status, and other request properties." + inputs: + - name: id + description: ID of department to return leave requests for. + type: string + in: path + required: true + - name: exclude_cancelled_requests + description: Don't return cancelled leave requests. + type: boolean + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_department_leave_requests + description: Get department leave requests request + stepFunction: + functionName: request + parameters: + url: /departments/${inputs.id}/leave_requests + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: exclude_cancelled_requests + value: '{{$.inputs.exclude_cancelled_requests}}' + in: query + condition: '{{present($.inputs.exclude_cancelled_requests)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_department_leave_requests.output.data + - actionId: list_department_salaries + categories: + - hris + actionType: get + label: Get Department Salaries + description: Get salaries for a specific department by ID + details: "Returns a paginated list of salaries for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns salary data including amounts, effective dates, and other salary properties." + inputs: + - name: id + description: The ID of the department whose salaries to retrieve. + type: string + in: path + required: true + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_department_salaries + description: Get department salaries request + stepFunction: + functionName: request + parameters: + url: /departments/${inputs.id}/salaries + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_department_salaries.output.data + - actionId: list_divisions + categories: + - hris + actionType: list + label: List Divisions + description: Get a list of divisions + details: "Returns a paginated list of all divisions in the BreatheHR system. Supports pagination with page and per_page. Returns division data including ID, name, and other division properties." + steps: + - stepId: get_all_divisions + description: Get divisions request + stepFunction: + functionName: request + parameters: + url: /divisions + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + response: + collection: true + result: + data: $.steps.get_all_divisions.output.data + - actionId: list_employee_jobs + categories: + - hris + actionType: list + label: List Employee Jobs + description: Get a list of employee jobs + details: "Returns a paginated list of all employee jobs in the BreatheHR system. Supports filtering by employee_id and pagination with page and per_page. Returns employee job data including ID, employee_id, job_title, start_date, end_date, and other job properties." + inputs: + - name: employee_id + description: Return jobs for this employee only. + type: number + in: query + required: false + - name: page + description: Page of results to fetch. + type: number + in: query + required: false + - name: per_page + description: Number of results to return per page. + type: number + in: query + required: false + steps: + - stepId: get_all_employee_jobs + description: Get employee jobs request + stepFunction: + functionName: request + parameters: + url: /employee_jobs + method: get + args: + - name: X-Api-Key + value: $.credentials.apiKey + in: headers + - name: employee_id + value: '{{$.inputs.employee_id}}' + in: query + condition: '{{present($.inputs.employee_id)}}' + - name: page + value: '{{$.inputs.page}}' + in: query + condition: '{{present($.inputs.page)}}' + - name: per_page + value: '{{$.inputs.per_page}}' + in: query + condition: '{{present($.inputs.per_page)}}' + response: + collection: true + result: + data: $.steps.get_all_employee_jobs.output.data From 2e0bc10ec5df798da37047b5c29fe2623424894c Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 09:09:30 +0000 Subject: [PATCH 05/13] feat: update claude.md --- CLAUDE.md | 1205 +++++------------------------------------------------ 1 file changed, 110 insertions(+), 1095 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f226a73..c90edba 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,56 +1,19 @@ ---- -name: falcon-config-builder -description: Expert agent for building comprehensive, tested Falcon API connector configurations with autonomous research and validation. ---- +# Falcon Connector Template -# Falcon Connector Configuration Guide +A YAML-based framework for building API connector configurations using the Falcon engine. Connectors define how StackOne communicates with third-party provider APIs. -You are an expert Falcon API configuration builder specializing in creating production-ready connector configurations. +## Available Plugins -## Table of Contents -- [Persona](#persona) -- [Project Knowledge](#project-knowledge) -- [Available Skills](#available-skills) -- [Core Principles](#core-principles) -- [Critical Workflow](#critical-workflow) -- [Research Phase](#research-phase) -- [Config Building](#config-building) -- [Testing & Validation](#testing--validation) -- [Security](#security) -- [Quick Reference](#quick-reference) -- [Tools Available](#tools-available) -- [Standards](#standards) -- [Boundaries](#boundaries) -- [Success Criteria](#success-criteria) +Two plugins are available depending on the connector type: ---- +- **`stackone-connector-builder`** — invoke with `/build-connector` — for generic (non-unified) connectors that return raw provider responses as-is +- **`stackone-unified-builder`** — invoke with `/build-unified-connector` — for connectors that map provider data to StackOne unified schemas (HRIS, ATS, CRM, etc.) -## Persona +Each plugin has detailed reference files in its `references/` directory covering auth patterns, step functions, expressions, and YAML structure. -- You specialize in building YAML-based API connector configurations using the Falcon framework -- You understand API authentication patterns, endpoint discovery, field mapping, and integration testing -- Your output: Comprehensive, tested YAML configurations that provide maximum API coverage and customer value -- You follow strict workflows to ensure quality, completeness, and security +## Legacy Skills -## Project Knowledge - -**Tech Stack:** -- Falcon API Config Framework (YAML-based connector configurations) -- StackOne CLI (`@stackone/cli`) for validation and testing -- Node.js/TypeScript environment -- MCP (Model Context Protocol) tools for research and testing - -**File Structure:** -- `src/configs/` – Provider-specific folders with YAML connector configs - -**Naming Convention:** -- Provider folder: `src/configs/provider-name/` (lowercase) -- Config file: `provider.connector.s1.yaml` -- Example: `src/configs/slack/slack.connector.s1.yaml` - -## Available Skills - -This project has **skills** - documented workflows that you should follow when performing specific tasks. These are located in the `.claude/skills/` directory. +Detailed step-by-step skills also exist in `.claude/skills/` for manual workflows. These are supplementary to the plugins above. ### Falcon Connector Build Skill @@ -91,115 +54,34 @@ This project has **skills** - documented workflows that you should follow when p **Location**: `.claude/skills/falcon-auth-setup.md` -**Summary**: Configure authentication for Falcon connectors: -- Framework limitations (only `custom` and `oauth2` types) -- Quick decision guide (token exchange vs static credentials) -- Authentication analysis (verify actual flow, not terminology) -- Custom headers must go in action args -- API Key, OAuth 2.0, Client Credentials patterns +**Summary**: Configure authentication for Falcon connectors (only `custom` and `oauth2` types supported). See plugin `references/auth-patterns.md` for detailed examples. ### Falcon Technical Reference Skill -**When to use**: User asks for "YAML syntax", "how to write step functions", "expression formats", "JSONPath vs JEXL", or general technical details. +**When to use**: User asks for "YAML syntax", "how to write step functions", "expression formats", "JSONPath vs JEXL" **Location**: `.claude/skills/falcon-technical-reference.md` -**Summary**: Comprehensive technical reference for Falcon connector YAML structure, step functions, expression formats, and best practices: -- File structure and partials ($ref) -- Actions configuration (Inputs, Results) -- Step Functions (Request, Paginated Request, SOAP, Group Data, Map, Typecast) -- Dynamic Values (JSONPath, String Interpolation, JEXL) -- GraphQL Best Practices - ---- - -## Unified Connector Skills - -These skills are for building connectors that map provider data to **customer-defined schemas** with unified pagination. Use these when the customer wants standardized output across multiple providers. - -### Unified Connector Build Skill - -**When to use**: User asks to "build unified connector", "standardized connector", "schema mapping", or needs to map provider data to a specific output schema. - -**Location**: `.claude/skills/unified-connector-build.md` - -**Summary**: Complete workflow for building unified/standardized connectors: -1. Define output schema first (schema-first approach) -2. Research provider endpoints with trade-off analysis -3. Analyze scope requirements (narrower is better) -4. Map fields to schema using `fieldConfigs` -5. Configure unified pagination with `cursor` -6. Build connector with `map_fields` and `typecast` steps -7. Validate configuration -8. Test and validate mappings -9. Document schema coverage - -**Key Difference**: Unified connectors use `schemaType: unified` and transform provider data to match YOUR schema field names. - -### Unified Field Mapping Skill - -**When to use**: User asks about "field mapping", "fieldConfigs", "enumMapper", "map fields", or needs to translate provider fields to schema fields. - -**Location**: `.claude/skills/unified-field-mapping.md` - -**Summary**: Detailed guide for mapping provider fields to unified schemas: -- `fieldConfigs` structure (`targetFieldKey` = YOUR schema name) -- Enum mapping with `enumMapper` (translate provider values) -- Nested objects and arrays -- JEXL transformations for computed fields -- Common mistakes (using provider field names instead of schema names) - -**Critical Rule**: `targetFieldKey` must ALWAYS use YOUR schema field names, never the provider's field names. - -### Unified Scope Decisions Skill - -**When to use**: User asks about "scopes", "permissions", "endpoint selection", "trade-offs", or needs to decide between multiple endpoint options. - -**Location**: `.claude/skills/unified-scope-decisions.md` - -**Summary**: Decision framework for selecting endpoints and scopes: -- Narrow scopes are always preferred (easier customer approval) -- Never use deprecated endpoints (even if they seem easier) -- Endpoint evaluation criteria (scopes, performance, data depth) -- `scopeDefinitions` syntax (NOT `scope_definitions` - this is a common error) -- Performance vs security trade-off analysis - -**Common Error**: Using `scope_definitions` instead of `scopeDefinitions` (camelCase is correct). - -### Unified Connector Testing Skill - -**When to use**: User asks to "test unified connector", "validate mapping", "debug fields", "test pagination", or output doesn't match expected schema. +**Summary**: Comprehensive technical reference. See plugin `references/` for the most up-to-date patterns. -**Location**: `.claude/skills/unified-connector-testing.md` +### Unified Connector Skills -**Summary**: Testing and validation for unified connectors: -- Phase 1: Raw response validation (understand provider structure) -- Phase 2: Field mapping validation (verify schema field names) -- Phase 3: Pagination testing (cursor, next page, empty results) -- Phase 4: Schema completeness validation -- Debugging techniques for common mapping issues +For building connectors that map provider data to **customer-defined schemas** with unified pagination: -**Key Debug Technique**: Use `--debug` flag to see raw provider response before mapping. - ---- +- **Build**: `.claude/skills/unified-connector-build.md` — Schema-first workflow with `fieldConfigs` and unified pagination +- **Field Mapping**: `.claude/skills/unified-field-mapping.md` — `fieldConfigs`, `enumMapper`, nested objects, JEXL transforms +- **Scope Decisions**: `.claude/skills/unified-scope-decisions.md` — Endpoint selection and scope trade-off analysis +- **Testing**: `.claude/skills/unified-connector-testing.md` — Field mapping validation, pagination testing, schema completeness ## Core Principles -**Specialization:** -- Build YAML-based API connector configurations using the Falcon framework -- Master API authentication patterns, endpoint discovery, field mapping, and integration testing -- Deliver comprehensive, tested YAML configurations with maximum API coverage - -**Philosophy:** -- **MAXIMUM COVERAGE**: Discover ALL useful actions providing customer value -- **ACTION-FOCUSED**: What actions would developers commonly perform? -- **CUSTOMER VALUE**: Prioritize operations solving real business problems -- **MORE IS BETTER**: Default to comprehensiveness over minimalism -- **PRACTICAL UTILITY**: Focus on production-ready operations - ---- +- **MAXIMUM COVERAGE**: Discover and implement ALL useful actions the provider API offers +- **ACTION-FOCUSED**: Prioritize operations that solve real business problems +- **DEFAULT TO NON-UNIFIED**: Unless explicitly requested, use `actionType: custom` +- **PRACTICAL UTILITY**: Focus on production-ready operations; ignore deprecated/internal endpoints +- **LEAVE NO TRACE**: Clean up test data after testing; scramble credentials when done -## 🔴 Critical Workflow (STRICT ORDER) +## Critical Workflow (STRICT ORDER) Follow this **exact sequence** when building Falcon API configurations: @@ -208,748 +90,91 @@ Follow this **exact sequence** when building Falcon API configurations: 3. **Version Validation** → `analyze_versioning()` → Detect/resolve API version conflicts 4. **Config Building** → Create comprehensive YAML with all discovered operations 5. **YAML Validation** → `stackone validate src/configs//.connector.s1.yaml` -6. **Coverage Validation** → `check_all_endpoints()` → Confirm ≥80% coverage +6. **Coverage Validation** → Confirm ≥80% coverage 7. **Action Tracking Setup** → **MANDATORY** - Save action inventory to `/tmp/_actions_tracking.json` 8. **Testing Phase** → `test_actions()` → Test EVERY operation with real API calls for EVERY auth type -9. **Test Completion Verification** → `check_test_completion()` → Verify 100% coverage +9. **Test Completion Verification** → Verify 100% coverage 10. **Security** → `scramble_credentials()` → Secure all credentials 11. **Meta Feedback** → `meta_feedback()` → **MANDATORY** - Send feedback for tracking -**❌ Skip/Disorder = Incomplete Task** - ---- - -## Research Phase - -### Quick Reference Workflow +**Skip/Disorder = Incomplete Task** -**Step 0:** Reference existing connector → `src/configs/` (same category/auth type) -**Step 1:** StackOne context → `get_stackone_categories()`, `get_stackone_actions(category)` -**Step 2:** Action discovery → `discover_actions()` (PRIMARY - autonomous subagent) -**Step 3:** Auth research → `vector_search()`, `web_search()` -**Step 4:** Documentation → `get_provider_coverage()`, `fetch()`, `extract_oas_actions()` -**Step 5:** External repos → `get_external_integrations()`, `scan_external_repo()` -**Step 6:** Parallel execution → Synchronize all results +## File Structure -### Action Discovery (PRIMARY) +Always use the **partials approach** — never monolithic files. -**Check S3 first:** -```typescript -map_provider_key("provider_name") → Get exact provider key -get_provider_actions("provider_key") → Check for indexed data ``` - -**Launch autonomous discovery if no data exists:** -```typescript -// Launch (returns immediately) -discover_actions({ provider: "provider_name", maxIterations: 30 }) -→ { taskId: "rpc_xxx", ... } - -// Poll every 60-90 seconds -get_discover_actions_task_status(taskId, provider) -→ Status: "pending" → "running" → "complete" - -// Extract results (5-15 minutes) -→ Result: JSON report with ~100 discovered actions -→ Auto-saved to S3 for future use -``` - -**Benefits:** Autonomous (20+ tool calls), Comprehensive, Async (5-15 min), Persistent - -### Version Validation - -After action discovery completes: - -```typescript -// Extract endpoints from discovered actions -const endpoints = discoveredActions.map(a => a.endpoints[0]); - -// Launch versioning analysis (2-5 minutes) -analyze_versioning({ provider: "provider_name", endpoints, maxIterations: 5 }) -→ { taskId: "rpc_xxx", ... } - -// Poll for status -get_analyze_versioning_task_status(taskId, provider) -→ Result: Version analysis with conflicts, migrations, recommendations +src/configs/{provider}/ + {provider}.connector.s1.yaml # Main connector config (auth, metadata) + {provider}.{resource}.s1.partial.yaml # One partial per resource/domain ``` -**Checklist:** -- [ ] Endpoints extracted from discovered actions -- [ ] Version analysis complete with recommendations -- [ ] Breaking changes and conflicts reviewed -- [ ] Recommended version identified for each endpoint -- [ ] Migration steps documented for deprecated endpoints - -### Parallel Execution Strategy +- Folder names: **lowercase, no hyphens** (e.g., `smartrecruiters/`) +- Partial files start directly with `- actionId:` — NO `actions:` wrapper -1. **Minute 0:** Launch `discover_actions(provider)` → Get taskId -2. **Minutes 0-5:** Complete Steps 0-5 (reference, context, auth, docs, repos) -3. **Minutes 5-15:** Poll `get_discover_actions_task_status()` every 60-90 seconds -4. **Minute 15:** Synchronize results -5. **Minute 15-20:** Run `analyze_versioning()` for version validation -6. **Begin config building** +## Key Rules ---- +- **camelCase** for ALL config field names (`scopeDefinitions`, NOT `scope_definitions`) +- Never use `:` in YAML string values — use parentheses or rephrase instead +- **2-space indentation**, no tabs +- Never use `type: array` — use `array: true` alongside the element type +- `value` fields use JSONPath: `$.inputs.fieldName` +- `condition` fields use JEXL: `'{{present(inputs.fieldName)}}'` +- `entrypointUrl` / `entrypointHttpMethod`: unified actions ONLY (omit for non-unified) +- `response:` block: unified actions ONLY (omit for non-unified) +- `context` field was renamed to `resources` (v2.2.0 breaking change) -## Config Building +## Authentication -### Prerequisites & Guidelines +Only two auth types exist in Falcon: -**Important:** -- **Default to non-unified actions** unless explicitly told otherwise - - Non-unified: Map exactly to provider's API, output provider's response entirely - - Unified: Use StackOne schema - only use when explicitly requested -- **Ignore deprecated actions, fields, and inputs** -- **YAML Best Practice:** Never use `:` as a literal value (use parentheses instead) - - ✅ `description: Filter by status (pending, approved)` - - ❌ `description: Filter by status: pending` - -### File Structure (ALWAYS Use Partials) - -**⚠️ ALWAYS use the partials approach - never create monolithic connector files.** - -``` -src/configs/{provider}/ -├── {provider}.connector.s1.yaml # Main: info, auth, $refs only -└── {provider}.{resource}.s1.partial.yaml # Actions grouped by resource -``` +- **`custom`** — No token exchange needed (API keys, basic auth, bearer tokens passed directly) +- **`oauth2`** — Requires a token exchange flow (authorization code, client credentials, etc.) -**Format Rules:** -- Lowercase, single word or compound word (no hyphens) for provider folder names -- Partial files start with `- actionId` (NOT `actions:` key) - -**Example:** -``` -src/configs/clickup/ -├── clickup.connector.s1.yaml # Main file -├── clickup.tasks.s1.partial.yaml # Task actions -└── clickup.users.s1.partial.yaml # User actions -``` - -❌ **WRONG:** Do NOT include `actions:` wrapper in partials - -### YAML Structure - -```yaml -# Main file: provider.connector.s1.yaml -StackOne: 1.0.0 -info: - title: Provider # Provider display name - key: provider # Unique identifier (lowercase) - version: 1.0.0 # Connector version - assets: - icon: https://stackone-logos.com/api/provider_name/filled/png - description: Brief description - -baseUrl: https://api.provider.com # Base URL for all API calls -releaseStage: preview # Always set to preview (manually changed after QA) - -# Optional: Rate limiting -rateLimit: - mainRatelimit: 10 # Requests per second - -resources: https://api.provider.com/docs - -# OAuth scopes -scopeDefinitions: - resource.write: - description: Allow writing and modifying resource - includes: resource.read # Space-separated list - resource.read: - description: Allow reading resource - -authentication: - - oauth2: ... - -# Only use `$ref: provider.resource` entries (alphabetical order) -actions: - $ref: provider.resource1 - $ref: provider.resource2 - -# Partial: provider.tasks.s1.partial.yaml -- actionId: list_resource - ... - inputs: ... - steps: ... - result: - data: $.steps.step1.output.data -``` - -### Authentication - -**⚠️ Only two authentication types supported:** -- `type: custom` - Static credentials (API keys, Basic auth) with NO token exchange -- `type: oauth2` - Authorization code or client credentials flows with token exchange +Analyze the provider's ACTUAL auth flow, not their marketing terminology. See plugin `references/auth-patterns.md` for detailed patterns and examples. **Decision Guide:** -- Token exchange via endpoint call? → `oauth2` with `grantType: client_credentials` and `refreshAuthentication` -- No token exchange? → `custom` with `authorization.type: basic|bearer|apiKey` -- Custom headers (not Authorization)? → `authorization.type: none` + define headers in action `args` of EVERY request step - -**⚠️ CRITICAL:** Analyze actual authentication flow, not provider terminology. Always verify token exchange process. +- Token exchange via endpoint call? → `oauth2` +- No token exchange? → `custom` +- Custom headers (not Authorization)? → `authorization.type: none` + define headers in action `args` **Field Types:** -- `setupFields`: T1-facing (OAuth apps, multi-tenant credentials) e.g., Client ID, Client Secret, scopes -- `configFields`: T2-facing (end user credentials) e.g., API token, user-specific sub-domain -- `label`: `API Key` or `OAuth 2.0` (only add descriptive text if necessary to differentiate) - -**Test Actions:** Verify credentials when user connects account -- `action`: The action ID to execute for testing -- `required`: If `true`, the test must pass for connection establishment - -#### API Key Example - -```yaml -authentication: - - custom: - type: custom - label: API Key - support: - link: https://hub.stackone.com/connection-guides/ticketing/jira - description: Admin privileges required - authorization: - type: basic - username: $.credentials.email - password: $.credentials.accessToken - configFields: - - key: accessToken - label: API Token - type: password - required: true - secret: true - placeholder: ATATT3xFfGF0aQNaJZQ9JtSvQ_example - description: Generate via Atlassian Account Settings > Security > Create and manage API tokens - tooltip: Save securely, won't be shown again - environments: - - key: production - name: Production - testActions: - - action: list_users - required: true -``` - -#### OAuth 2.0 Examples - -**Typical OAuth:** `asana`, `gmail`, `xero` (Authorization code → Access Token → Refresh Token) -**Complex:** `jira` (includes post-auth call) -**Client Credentials:** `greenhouse`, `globalizationpartners`, `bigchange` - -**Note:** If refresh token request doesn't require authorization headers: -```yaml -authorization: - type: none -``` - -### Scope Definitions - -```yaml -scopeDefinitions: - https://www.googleapis.com/auth/drive: - description: View and manage all your Drive files - includes: https://www.googleapis.com/auth/drive.readonly - https://www.googleapis.com/auth/drive.readonly: - description: View all your Drive files -``` - -**Rules:** -- Only include relationships explicitly documented by provider -- Read-write scope typically includes readonly version -- Broader scope typically includes narrower scopes -- Read-only scopes should NOT include write scopes - -### Actions - -**Required fields:** -- `actionId`: Unique action reference -- `categories`: List of categories for StackOne UI -- `actionType`: `custom` (default, non-unified) or `list|get|create|update|delete` (unified only) -- `label`: Human-readable name -- `description`: Short description (shown in UI) -- `details`: Longer description (tool description) -- `resources`: Action-specific documentation URLs -- `steps`: List of step functions -- `result`: Final action output - -**Optional fields:** -- `inputs`: Request parameters (path, query, body) -- `requiredScopes`: (Mandatory if provider uses scopes) Space-separated OAuth scopes (must be in `scopeDefinitions`). Use most restrictive scope that allows the action to work. -- `entrypointUrl`/`entrypointHttpMethod`: Only for unified actions (DO NOT USE for non-unified) - -### Inputs - -**⚠️ For non-unified actions: inputs must match exactly the provider's request parameters.** -**DO NOT CREATE INPUTS THAT DO NOT EXIST IN THE PROVIDER API.** -**Ignore deprecated actions/fields/inputs.** - -Reference with JSONPath: `$.inputs.fieldName` (preferred) or JEXL `'{{inputs.fieldName}}'` for conditional logic/string construction. - -**Types:** `string`, `number`, `boolean`, `datetime_string`, `object`, `enum` -**Never use `type: array`** - always use `array: true` with element type - -**Object type:** -```yaml -inputs: - - name: items - description: Item object - type: object - in: body - required: true - properties: - - name: name - description: Name of item - type: string - required: false -``` - -**Array fields:** -```yaml -inputs: - - name: userIds - description: Array of user IDs - type: string # Element type - array: true - in: body - required: true -``` - -**Enum fields:** -```yaml -inputs: - - name: status - description: Employment status - type: enum - required: true - in: query - oneOf: - values: - - active - - inactive - - terminated -``` - -### Field Configs (Unified Actions Only) - -**NOTE: `fieldConfigs` are NOT required for non-unified connectors!** - -Maps provider response to StackOne unified response: - -```yaml -fieldConfigs: - - targetFieldKey: id - expression: $.accountId - type: string - - targetFieldKey: type - expression: $.accountType - type: enum - enumMapper: - matcher: - - matchExpression: '{{$.accountType == "atlassian"}}' - value: agent - - matchExpression: '{{$.accountType == "app"}}' - value: bot - - targetFieldKey: active - expression: $.active - type: boolean -``` - -### Steps - -**⚠️ Every step must have a `description` field.** -**⚠️ Custom authentication headers must be in `args` of every action's request step.** - -```yaml -steps: - - stepId: list_users - description: List users - stepFunction: - functionName: request - parameters: - url: '/users' - method: get - args: - - name: showInactive - value: $.inputs.showInactive - in: body - condition: '{{present(inputs.showInactive)}}' - - name: accept - value: application/json - in: headers -``` - -### Step Functions - -Defined in `packages/core/src/stepFunctions/stepFunctionsList.ts`. - -#### Request - -**Always use `args` for parameters (never direct `body` field).** - -**⚠️ IMPORTANT:** -- **For `value` fields**: Use JSONPath `value: $.inputs.fieldName` -- **For `condition` fields**: Use JEXL `condition: "{{present(inputs.fieldName)}}"` -- **Never use JEXL `'{{inputs.fieldName}}'` for `value` fields** - -```yaml -stepFunction: - functionName: request - parameters: - url: '/users' - method: post - args: - - name: email - value: $.inputs.email - in: body - - name: phone - value: $.inputs.phone - in: body - condition: '{{present(inputs.phone)}}' - customErrors: # Optional error remapping - - receivedStatus: 404 - targetStatus: 400 - message: 'Custom error message' -``` - -**Raw Array Bodies:** -When API requires raw JSON array `[...]` (not wrapped in object), use `spread: true`: - -```yaml -args: - - name: events - value: $.inputs.events - in: body - spread: true # Sends [{...}, {...}] instead of {"events": [...]} -``` - -**Note:** `response:` is ONLY for unified actions. DO NOT USE FOR NON-UNIFIED ACTIONS. - -#### Paginated Request - -**Only use if provider supports cursor-based pagination. Otherwise use `request`.** - -```yaml -stepFunction: - functionName: paginated_request - parameters: - url: "/application.list" - method: post - response: - dataKey: results - nextKey: nextCursor # Field containing next cursor - iterator: - key: cursor # How cursor is mapped into request - in: body -``` - -#### SOAP Request - -**Key parameters:** -- `soapOperation` - SOAP operation name -- `soapAction` - SOAP action URI (usually namespace + operation name) -- `namespaces` - XML namespace definitions -- `args` - Arguments in SOAP request body -- `useSoapContext` - Set to `false` when provider expects payload as-is - -**Important:** -- Use string interpolation (`${inputs.fieldName}`) for dynamic values -- Prefix XML attributes with `@_` (e.g., `@_xsi:type`) to render as attributes not child nodes -- Keep credential blocks inside request payload if provider requires them on every call - -```yaml -stepFunction: - functionName: soap_request - parameters: - url: /EmployeeService - method: post - soapOperation: GetEmployee - soapAction: http://example.com/soap/GetEmployee - useSoapContext: false - namespaces: - - namespaceIdentifier: emp - namespace: http://example.com/employees - args: - - name: EmployeeId - value: ${inputs.employee_id} - in: body -``` - -#### Other Step Functions - -- `group_data`: Groups data from multiple steps -- `map_fields`: Maps using `fieldConfigs` (non-unified actions don't need this) -- `typecast`: Applies types from `fieldConfigs` (non-unified actions don't need this) - -### Expression Formats - -**1. JSONPath (`$.path.to.field`) - PREFERRED** - -For direct references (no string construction): -- Credentials: `token: $.credentials.apiKey` -- Inputs: `value: $.inputs.userId` -- Step output: `dataSource: $.steps.fetch_users.output.data` - -**2. String Interpolation (`${...}`)** - -For embedding dynamic values within strings: -- URLs: `url: /users/${inputs.id}` -- Multiple segments: `url: /users/${inputs.userId}/posts/${inputs.postId}` -- Domains: `baseUrl: https://${credentials.domain}.api.com` - -**3. JEXL Expressions (`'{{...}}'`)** - -**ONLY USE JEXL EXPRESSIONS DEFINED IN THIS PACKAGE:** -https://github.com/StackOneHQ/connect/tree/main/packages/expressions - -For conditional logic, transformations, complex expressions (wrap in single quotes): - -```yaml -condition: '{{present(inputs.includeInactive)}}' -value: '{{inputs.name.toUpperCase()}}' -value: '{{$.status == "active" ? "enabled" : "disabled"}}' -value: '{{$.count > 0 ? $.count : "none"}}' -``` - -**When to use JEXL:** -- Conditional logic in `condition` fields -- Enum matching with `matchExpression` -- String transformations, math, ternary operators - -### GraphQL API Best Practices - -Reference: `linear` connector - -#### Input Structure - -Always use nested `variables` object: - -```yaml -inputs: - - name: variables - description: Variables for the query - type: object - in: body - properties: - - name: first - description: Number of items to forward paginate - type: number - required: false - - name: filter - description: Filter object - type: object - required: false -``` - -#### Request Configuration - -```yaml -steps: - - stepId: fetch_resource - description: Query resource from GraphQL API - stepFunction: - functionName: request - parameters: - url: "/graphql" - method: post - args: - - name: Content-Type - value: application/json - in: headers - - name: query - value: "query($first: Int, $filter: FilterType) { resources(first: $first, filter: $filter) { nodes { id name } } }" - in: body - - name: variables - in: body - condition: "{{present(inputs.variables)}}" - value: - { - first: $.inputs.variables.first, - filter: $.inputs.variables.filter - } -``` - -#### Nested Objects in Queries - -**⚠️ IMPORTANT: When querying nested objects, ONLY return the `id` field if a separate action exists to fetch the full object.** - -Applies to both GraphQL and REST APIs. - -**Correct:** -```yaml -# If get_user action exists, only return id in nested user objects -value: "query($id: String!) { issue(id: $id) { id title assignee { id } creator { id } team { id } } }" -``` - -**Incorrect:** -```yaml -# Don't return full nested objects if separate actions exist -value: "query($id: String!) { issue(id: $id) { id title assignee { id name email } creator { id name email } team { id name } } }" -``` - -#### Query String Format - -**List/Query Actions:** -```yaml -value: "query($first: Int, $after: String, $filter: ResourceFilter) { resources(first: $first, after: $after, filter: $filter) { nodes { id name } pageInfo { hasNextPage endCursor } } }" -``` - -**Get Actions:** -```yaml -value: "query($id: String!) { resource(id: $id) { id name description } }" -``` - -**Mutation Actions:** -```yaml -value: "mutation($input: ResourceCreateInput!) { resourceCreate(input: $input) { success resource { id name } } }" -``` - -**Update Mutations:** -```yaml -value: "mutation($id: String!, $input: ResourceUpdateInput!) { resourceUpdate(id: $id, input: $input) { success resource { id name } } }" -``` - -#### Mutation Input Objects - -For mutations with input objects, nest the input structure: - -```yaml -- name: variables - value: - { - id: $.inputs.id, - input: - { - title: $.inputs.variables.title, - description: $.inputs.variables.description, - assigneeId: $.inputs.variables.assigneeId - } - } - in: body -``` - -### Result - -```yaml -# Read response -result: - data: $.steps.step_id.output.data - -# Write response -result: - message: Resource updated successfully - data: - id: $.inputs.id # Use JSONPath for direct references -``` - ---- +- `setupFields`: T1-facing (OAuth apps, multi-tenant credentials) +- `configFields`: T2-facing (end user credentials) ## Testing & Validation ### YAML Validation (MANDATORY) -**CRITICAL:** Config MUST pass validation before proceeding to testing. - ```bash npm install -g @stackone/cli stackone validate [pathToYaml] ``` -**Configuration Requirements:** -- Map ALL actions discovered through `discover_actions` subagent -- Use version-validated endpoints from `analyze_versioning()` -- Include all relevant operations from `get_stackone_actions()` -- Include comprehensive CRUD operations where applicable -- Add error handling and rate limiting -- Use proper credential templating: `${credentials.field}` -- Write clear, concise descriptions (1-2 sentences) for connector, operations, steps, fields - -**Fix Resources (Use in Order):** -1. "Config Building" section above -2. Similar connectors (same auth type or structure) -3. Provider API documentation -4. Working actions in same connector -5. `src/configs/DEVELOPERS.md` - -### Action Tracking Setup (MANDATORY) - -**CRITICAL:** Before testing begins, create `/tmp/_actions_tracking.json` with complete action inventory. - -**Structure:** -```json -{ - "provider": "provider_name", - "auth_types": ["oauth2", "api_key"], - "total_actions": 15, - "actions": [ - { - "authKey": "oauth2", - "operation_name": "list_employees", - "operation_path": "/employees", - "method": "GET", - "actionType": "LIST", - "tested": false, - "test_results": {}, - "capturedData": {} - } - ], - "testing_progress": { - "total_required_tests": 30, - "tests_completed": 0, - "percentage_complete": 0 - } -} -``` - -**Formula:** `total_required_tests = operations × auth_types` - -**Update after each test:** Set `test_results[authKey] = {status, tested_at, response_code}`. Mark `tested: true` only when ALL auth types tested. - -**Verification (BLOCKING):** -- ✅ `testing_progress.percentage_complete === 100` -- ✅ No actions with `tested: false` -- ✅ All `test_results` show "success" for all auth types - -### Testing Phase (MANDATORY) - -**CRITICAL:** First, ask if testing should be read-only (list/get) or should include actions that mutate resources (create/update/delete). - -**Testing ideology - Leave no trace:** State and data should be as before testing began. Clean up any sample data generated. +Config MUST pass validation before proceeding to testing. -**CRITICAL:** You MUST test every operation with EVERY auth type. Partial testing is NOT acceptable. +### Action Tracking (MANDATORY) -**Action Type Guidelines:** -- **List:** If empty, use create actions to import test data then retry -- **Get:** Run after list actions, use pre-existing identifiers. **DO NOT MAKE UP INPUT PARAMS** -- **Create:** If pre-requisite data required, use existing list/get actions first -- **Update:** Test after respective create action. Revert system config changes immediately. **DO NOT UPDATE RESOURCES NOT CREATED DURING TESTING** -- **Delete:** Only delete resources generated by previous tests. **DO NOT DELETE RESOURCES NOT CREATED DURING TESTING** +Before testing begins, create `/tmp/_actions_tracking.json` with complete action inventory including all auth types. Formula: `total_required_tests = operations × auth_types`. -**For detailed systematic testing workflow, see:** `.claude/skills/test-connector/SKILL.md` +### Testing Phase -#### Testing Methods +**First, ask if testing should be read-only or include mutations (create/update/delete).** -**Option 1: Async Tool (Batch Testing)** -- Use `test_actions()` tool - executes `stackone run ...` commands -- Poll `get_test_actions_task_status()` every 30-60 seconds until complete -- Best for: Multiple operations, automated workflows +**Testing ideology — Leave no trace:** State and data should be as before testing began. -**Option 2: Manual CLI (Individual Actions)** -- `stackone run --connector --account --credentials --action-id [--params ] [--debug]` -- Best for: Immediate feedback, debugging specific actions +**You MUST test every operation with EVERY auth type. Partial testing is NOT acceptable.** -**Parameter Format:** -- LIST: `{"queryParams":{"max":10}}` -- GET: `{"path":{"id":"123"}}` -- CREATE: `{"body":{"title":"New"}}` -- UPDATE: `{"path":{"id":"123"},"body":{"name":"Updated"}}` -- DELETE: `{"path":{"id":"123"}}` - -#### Testing Execution - -**For EACH operation in tracking file:** +**Testing Cycles (Dependency Order):** +1. LIST (no dependencies) → Capture IDs +2. GET (use IDs from LIST) +3. CREATE (generate new resources) — Full mode only +4. UPDATE (use IDs from CREATE) — Full mode only +5. DELETE (clean up from CREATE) — Full mode only -1. Test with FIRST auth type → Update `test_results[authKey]` with status, timestamp, response code -2. Test with SECOND auth type → Update `test_results[authKey]` -3. After ALL auth types tested → Mark `tested: true`, update `testing_progress.tests_completed++` -4. If action fails → Fix YAML, re-test immediately +**Testing Methods:** +- **Async Tool**: `test_actions()` → poll `get_test_actions_task_status()` (best for batch testing) +- **Manual CLI**: `stackone run --connector --account --credentials --action-id [--params ] [--debug]` **Error Fix Strategy:** - **400:** Fix parameter structure/type/location @@ -958,297 +183,87 @@ stackone validate [pathToYaml] - **405:** Fix method or REMOVE action - **500:** Check request body/headers -**Testing Cycles (Dependency Order):** -1. LIST (no dependencies) → Capture IDs -2. GET (use IDs from LIST) -3. CREATE (generate new resources) - Full mode only -4. UPDATE (use IDs from CREATE) - Full mode only -5. DELETE (clean up from CREATE) - Full mode only +### Test Completion Verification (BLOCKING) -### Coverage Validation - -``` -check_all_endpoints(discoveredActions, stackOneOperations, config) -→ Must achieve ≥80% coverage before testing -``` - -### Test Completion Verification - -**BEFORE proceeding to security, verify:** - -**1. Using check_test_completion tool:** -``` -check_test_completion(allOperations, testedOperations) -→ Must achieve 100% before task completion -``` - -**2. Verify against tracking file:** -```bash -cat /tmp/_actions_tracking.json | jq '.testing_progress.percentage_complete' # MUST return: 100 -cat /tmp/_actions_tracking.json | jq '.actions[] | select(.tested == false)' # MUST return: empty -cat /tmp/_actions_tracking.json | jq '.actions[].test_results[][] | select(.status == "failed")' # MUST return: empty -``` +Before proceeding to security: +- `testing_progress.percentage_complete === 100` +- No actions with `tested: false` +- All `test_results` show "success" for all auth types **If ANY check fails, DO NOT PROCEED. Fix and re-test.** -### Success Criteria Checklist - -**Research & Discovery:** -- [ ] All useful actions discovered via `discover_actions` subagent -- [ ] StackOne operations catalogued via `get_stackone_actions()` -- [ ] External repos analyzed (≥2-3) -- [ ] API versions validated via `analyze_versioning()` subagent - -**Configuration:** -- [ ] All discovered actions mapped with correct versions -- [ ] Context docs with live links -- [ ] YAML validation passed - -**Action Tracking (MANDATORY):** -- [ ] Action tracking file created at `/tmp/_actions_tracking.json` -- [ ] All operations listed in tracking file -- [ ] All auth types identified and documented -- [ ] Total required tests calculated (operations × auth_types) - -**Testing (MANDATORY - 100% REQUIRED):** -- [ ] Every operation tested with EVERY auth type using `test_actions()` tool -- [ ] `get_test_actions_task_status()` polled until all tests complete -- [ ] Tracking file updated after each test batch -- [ ] All `test_results` show "success" status -- [ ] `testing_progress.percentage_complete === 100` -- [ ] No operations with `tested: false` in tracking file -- [ ] Coverage ≥80% via `check_all_endpoints()` -- [ ] 100% test completion via `check_test_completion()` - -**Security & Feedback:** -- [ ] Credentials scrambled via `scramble_credentials()` -- [ ] Meta feedback sent via `meta_feedback()` - MANDATORY - -**Final Verification (BLOCKING):** -- [ ] Read tracking file and confirm 100% completion -- [ ] Confirm zero failed tests in tracking file -- [ ] Confirm all auth types tested for all operations - ---- - ## Security -### Credential Scrambling (MANDATORY BEFORE STORAGE) +### Credential Scrambling (MANDATORY) -```javascript -// Step 1: Scramble credentials after successful testing -scramble_credentials({ - config: validatedConfigJson, - credentials: testCredentialsJson, - securityLevel: "PRODUCTION", // Use PRODUCTION for live configs -}); +After successful testing, call `scramble_credentials()` with `securityLevel: "PRODUCTION"`. Save ONLY scrambled versions. Delete originals. -// Step 2: Save ONLY scrambled versions -// - config: result.scrambledConfig -// - credentials: result.scrambledCredentials -// - metadata: result.detectedFields & warnings -``` - -**Security Checklist:** -- [ ] All credential patterns detected (check `result.detectedFields`) -- [ ] No warnings about missed fields (address `result.warnings`) -- [ ] PRODUCTION security level chosen for live configs -- [ ] Custom patterns added for provider-specific formats -- [ ] Scrambled output verified (no plaintext credentials) -- [ ] Original unscrambled configs deleted - -**⚠️ Security Anti-Patterns (NEVER DO THESE):** -- ❌ Committing unscrambled configs to git -- ❌ Sharing configs with real credentials -- ❌ Skipping scrambling "just for testing" -- ❌ Using DEBUG preset in production -- ❌ Ignoring warnings about undetected secrets +**Never:** +- Commit unscrambled configs to git +- Share configs with real credentials +- Skip scrambling "just for testing" ### Meta Feedback (MANDATORY) -**CRITICAL:** Call `meta_feedback()` after EVERY config generation completion, regardless of user preference. - -**Required Format:** -``` -Provider: [provider_name] -Status: [completed/failed/partial] - -STRENGTHS: -- [What worked well] - -IMPROVEMENTS NEEDED: -- [Issues that need fixing] -``` - -**Meta Feedback Requirements:** -- [ ] Always call `meta_feedback()` - No exceptions -- [ ] Include both positive AND negative feedback - -**⚠️ Anti-Patterns:** -- ❌ Skipping meta_feedback because user didn't ask -- ❌ Only reporting positive feedback - ---- - -## Quick Reference - -### YAML Best Practices - -**Config Field Names - camelCase Only**: -- ⚠️ ALL Falcon config field names use camelCase, never snake_case -- This applies to EVERY config field, not just specific ones -- Using snake_case causes silent failures or validation errors - -```yaml -# CORRECT - camelCase -scopeDefinitions: fieldConfigs: targetFieldKey: -enumMapper: matchExpression: dataKey: -nextKey: pageSize: indexField: -stepFunction: functionName: dataSource: -compositeIdentifiers: requiredScopes: customErrors: - -# WRONG - snake_case will fail -scope_definitions: field_configs: target_field_key: -``` - -**Reserved Characters**: -- ⚠️ Never use `:` character in YAML values (use parentheses instead) - - ❌ `description: Filter by status: active, inactive` - - ✅ `description: Filter by status (active, inactive)` - -**Default Behavior**: -- Build NON-UNIFIED actions (`actionType: custom`) unless explicitly told otherwise -- Ignore deprecated actions, fields, and inputs -- Always use action-specific documentation URLs in `resources` - ---- - -## Tools Available - -### Core Research -- `get_stackone_categories()`, `get_stackone_actions(category)` -- `get_docs()`, `map_provider_key(provider)`, `get_providers()` -- `get_provider_coverage(provider)` +Call `meta_feedback()` after EVERY config generation, regardless of user preference. Include provider name, status, strengths, and improvements needed. -### Action Discovery (PRIMARY) -- `discover_actions(provider, apiVersion?, maxIterations?)` - **PRIMARY TOOL** - Autonomous research (5-15 min) -- `get_discover_actions_task_status(taskId, provider)` - Poll status and retrieve results -- `get_provider_actions(provider)` - Check S3 for indexed actions +## Available MCP Tools -### API Versioning -- `analyze_versioning(provider, endpoints?, maxIterations?)` - **VERSION VALIDATION** - Detect conflicts (2-5 min) -- `get_analyze_versioning_task_status(taskId, provider)` - Poll status and retrieve results +### Research +- `get_stackone_categories` / `get_stackone_actions` — StackOne unified model info +- `map_provider_key` — Find a provider's key from its name +- `get_provider_actions` / `get_provider_coverage` — Existing provider coverage +- `get_providers` — List all providers +- `get_docs` — Fetch StackOne documentation -### Web Search -- `web_search(query)`, `vector_search(query, provider, k)` -- `fetch(url, headers?, extractText?)`, `extract_html_text(html)` +### Discovery & Analysis +- `discover_actions` / `get_discover_actions_task_status` — Auto-discover provider API actions (PRIMARY - 5-15 min async) +- `analyze_versioning` / `get_analyze_versioning_task_status` — Analyze API versioning strategy (2-5 min async) -### External Repository Analysis -- `get_external_integrations(provider, count?)` -- `analyze_external_integration(integration, provider)` -- `get_external_repos()`, `scan_external_repo(url, terms, options?)` +### Web & Search +- `web_search` / `vector_search` — Search the web or vector store +- `fetch` / `extract_html_text` — Fetch URLs and extract content -### Configuration & Templates -- `get_stackone_expressions()` -- `extract_oas_actions(oasContent, hasBeenTruncated, ...)` +### External Repos +- `get_external_integrations` / `analyze_external_integration` — Analyze existing integrations +- `scan_external_repo` / `search_external_repo` — Search external code repositories -### Testing & Validation -- `test_actions(config, maxIterations?, maxDurationMs?)` - **RECOMMENDED** - Async batch testing -- `get_test_actions_task_status(taskId, provider)` - Poll test status -- `check_all_endpoints(...)` - **MANDATORY** - Validate ≥80% coverage -- `check_test_completion(...)` - **MANDATORY** - Verify 100% operations tested +### Testing +- `test_actions` / `get_test_actions_task_status` — Run connector action tests (async batch) - **Manual CLI**: `stackone run --connector --account --credentials --action-id [--params ] [--debug]` ### Description Improvement -- `improve_descriptions(config, maxIterations?)` - Async tool to improve YAML descriptions (2-5 min) -- `get_improve_descriptions_task_status(taskId, provider)` - Poll status and retrieve improved YAML +- `improve_descriptions` / `get_improve_descriptions_task_status` — Improve YAML descriptions (async) -### Security (MANDATORY) -- `scramble_credentials(config?, credentials?, securityLevel, customPatterns?)` - **REQUIRED** - Secure credentials before storage - -### Meta Feedback (MANDATORY) -- `meta_feedback(feedback, tool_names)` - **REQUIRED** - Send feedback to LangSmith for tracking +### Security & Feedback +- `scramble_credentials` — Scramble stored credentials after use (MANDATORY) +- `meta_feedback` — Submit feedback for tracking (MANDATORY) ### CLI Validation -- `stackone validate ` - Validate YAML syntax and structure - ---- - -## Standards - -**YAML Structure**: -- Follow examples in existing connectors -- Define authentication ONCE at top level -- Use proper indentation (2 spaces) -- Use `$.credentials.field` for credential templating -- Include clear descriptions for all operations - -**Naming Conventions**: -- Provider folders: lowercase with hyphens (`provider-name`) -- Config files: `provider.connector.s1.yaml` -- Partial files: `provider.resource.s1.partial.yaml` - -**Quality Standards**: -- Clear descriptions (1-2 sentences) for operations, steps, fields -- Proper structure with error handling -- Use appropriate step functions -- Consistent wording, active voice, no redundancy - ---- +- `stackone validate ` — Validate YAML syntax and structure ## Boundaries -**✅ Always**: -- Follow the 11-step CRITICAL WORKFLOW in exact order +**Always:** +- Follow the 11-step Critical Workflow in exact order - Create action tracking file before testing - Test every operation with EVERY auth type -- Update tracking file after each test -- Verify 100% completion against tracking file - Validate YAML before testing - Scramble credentials before storage - Send meta feedback -**⚠️ Ask First**: +**Ask First:** - Skipping workflow steps - Using untested operations - Storing unscrambled credentials - Proceeding with partial test coverage -**🚫 Never**: +**Never:** - Skip action tracking file creation - Skip testing any auth type -- Test only one auth type when multiple exist -- Skip tracking file updates during testing -- Proceed without 100% test coverage verified in tracking file +- Proceed without 100% test coverage - Skip `discover_actions` for research -- Skip `analyze_versioning` for version conflicts - Skip `scramble_credentials` - Skip `meta_feedback` - Commit plaintext credentials - Ignore validation errors - ---- - -## Success Criteria - -A successful Falcon configuration delivers: - -- **Comprehensive Action Coverage**: All useful actions developers need -- **Version-Validated Endpoints**: Correct API versions with conflict resolution -- **Complete Action Tracking**: Every operation documented with all auth types identified -- **Validated Functionality**: Every operation tested with real API calls for EVERY auth type (100% coverage verified) -- **Verifiable Testing**: Action tracking file shows `percentage_complete: 100` with zero failed tests -- **Market Insight**: Features differentiating StackOne from competitors -- **Secure**: All credentials properly scrambled before storage -- **Documented**: Clear sources and context for all implementations - -**Remember**: Autonomous Discovery + Version Validation + Maximum Coverage + Complete Multi-Auth Testing + Tracking Verification + Security = Customer Value - -**Testing Formula**: `Success = (operations × auth_types) tests completed at 100%` - ---- - -## Additional Resources - -- **Testing Skill:** `.claude/skills/test-connector/SKILL.md` - Detailed systematic testing workflow -- **Git Branching & Commit Format:** `README.md` From 7a3a3b2da5e30849c81b8082ce1cda5652fb6d49 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 10:11:45 +0000 Subject: [PATCH 06/13] fix: restoring some changes in line with new skills. Fixing plugins. --- .../stackone-unified-builder/README.md | 8 +- CLAUDE.md | 198 +++++++++++------- 2 files changed, 121 insertions(+), 85 deletions(-) diff --git a/.claude/plugins/stackone-unified-builder/README.md b/.claude/plugins/stackone-unified-builder/README.md index 680d08f..f2b4423 100644 --- a/.claude/plugins/stackone-unified-builder/README.md +++ b/.claude/plugins/stackone-unified-builder/README.md @@ -1,11 +1,11 @@ -# stackone-connector-builder +# stackone-unified-builder Interactive step-by-step wizard for building unified Falcon connectors. Guides external builders through every phase — from choosing an output schema to live API testing. ## Installation ```bash -/plugin install stackone-connector-builder@stackone-claude-marketplace +/plugin install stackone-unified-builder@stackone-claude-marketplace ``` ## Usage @@ -13,7 +13,7 @@ Interactive step-by-step wizard for building unified Falcon connectors. Guides e ### Full wizard (recommended) ```bash -/build-connector +/build-unified-connector ``` Walks through all 6 steps in sequence. Progress is saved to `.connector-build-session.json` so you can pause and resume at any time. @@ -34,7 +34,7 @@ Each step can also be run independently. It reads the session file and picks up ### Resume a session ```bash -/build-connector +/build-unified-connector ``` If a `.connector-build-session.json` exists, the wizard will offer to resume from where you left off. diff --git a/CLAUDE.md b/CLAUDE.md index cf99e24..4cac55b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,8 @@ Each plugin has detailed reference files in its `references/` directory covering | Scenario | Command/Trigger | |----------|----------------| | First time / unsure | `/on-boarding` | -| Schema-based connector (know what you want) | `start unified build for [provider]` | +| Generic (non-unified) connector | `/build-connector` | +| Schema-based (unified) connector | `/build-unified-connector` or `start unified build for [provider]` | | Agentic connector (know what you want) | Just describe what you want | | Test connector with AI agent | `/test-mcp-connector ` | @@ -56,7 +57,7 @@ When building Schema-Based connectors, follow this workflow: ``` ┌─────────────────────────────────────────────────────────────┐ │ 1. FORK CONNECTOR │ -│ └─ Run `stackone pull ` to get existing │ +│ └─ Run `npx @stackone/cli pull ` to get existing │ │ └─ Fork existing or create new from template │ ├─────────────────────────────────────────────────────────────┤ │ 2. BUILD AUTH │ @@ -102,7 +103,7 @@ When building Schema-Based connectors, follow this workflow: First, pull the existing connector from StackOne: ```bash # Pull existing connector from StackOne registry -stackone pull +npx @stackone/cli pull ``` If pull succeeds: Fork and modify the existing connector @@ -123,7 +124,7 @@ Before anything else, configure and validate authentication: Push connector and create a test account: ```bash # Push to your profile -stackone push src/configs//.connector.s1.yaml --profile +npx @stackone/cli push src/configs//.connector.s1.yaml --profile # User creates account in StackOne dashboard # Get account ID for testing @@ -131,7 +132,7 @@ stackone push src/configs//.connector.s1.yaml --profile =80%) 7. Action Tracking Setup 8. Testing Phase (100% coverage, all auth types) 9. Test Completion Verification @@ -246,7 +245,7 @@ This project has **skills** - documented workflows that you should follow when p **Summary**: Comprehensive testing workflow for Falcon connectors: - Create action tracking file (mandatory) - Test every operation with EVERY auth type -- Cycle-based testing (LIST → GET → CREATE → UPDATE → DELETE) +- Cycle-based testing (LIST -> GET -> CREATE -> UPDATE -> DELETE) - Fix failures immediately - Verify 100% completion before proceeding - Update tracking file after each test @@ -350,21 +349,55 @@ These skills are for building connectors that map provider data to **customer-de - **PRACTICAL UTILITY**: Focus on production-ready operations; ignore deprecated/internal endpoints - **LEAVE NO TRACE**: Clean up test data after testing; scramble credentials when done -## Critical Workflow (STRICT ORDER) +## Workflows + +### Plugin Workflows (Recommended) + +The plugins automate the full build process. Use these when possible: + +**Generic connector (`/build-connector`):** + +| Step | Command | What it does | +|------|---------|--------------| +| 1 | `/setup-connector` | Provider setup, checks StackOne index, CLI pull or scaffold | +| 2 | `/configure-auth` | Auth configuration (API key, OAuth2, Basic Auth, custom) | +| 3 | `/discover-actions` | Choose scoped or maximal discovery (MCP-powered) | +| 4 | `/build-config` | Generate YAML with `actionType: custom` for all actions | +| 5 | `/validate-connector` | Validate YAML structure | +| 6 | `/test-connector` | Live test + automatic cleanup + credential scrambling | + +**Unified connector (`/build-unified-connector`):** -Follow this **exact sequence** when building Falcon API configurations: +| Step | Command | What it does | +|------|---------|--------------| +| 1 | `/choose-schema` | Pick a StackOne built-in schema, point to your own, or define one interactively | +| 2 | `/check-connector` | Check if a base connector exists, pull it or scaffold a new one | +| 3 | `/scope-actions` | Choose which resources and CRUD operations to expose | +| 4 | `/map-fields` | Map provider API fields to your schema, writes YAML partials | +| 5 | `/validate-connector` | Validate the YAML config structure | +| 6 | `/test-connector` | Test live against the provider API and verify field output | -1. **Research Phase (PARALLEL)** → Launch `discover_actions` subagent + main agent for auth/docs/external repos -2. **Synchronization** → Collect and integrate subagent results -3. **Version Validation** → `analyze_versioning()` → Detect/resolve API version conflicts -4. **Config Building** → Create comprehensive YAML with all discovered operations -5. **YAML Validation** → `stackone validate src/configs//.connector.s1.yaml` -6. **Coverage Validation** → `check_all_endpoints()` → Confirm ≥80% coverage -7. **Action Tracking Setup** → **MANDATORY** - Save action inventory to `/tmp/_actions_tracking.json` -8. **Testing Phase** → `test_actions()` → Test EVERY operation with real API calls for EVERY auth type -9. **Test Completion Verification** → Verify 100% coverage -10. **Security** → `scramble_credentials()` → Secure all credentials -11. **Meta Feedback** → `meta_feedback()` → **MANDATORY** - Send feedback for tracking +**Plugin features:** +- **Session persistence** — progress is saved to `.connector-build-session.json` so you can pause and resume at any step +- **Two discovery modes** — *Scoped* (vector search, quick) or *Maximal* (async MCP, 5-15 min, full coverage) +- **Test cleanup** — every record created during testing is tracked and deleted afterwards +- **Automatic credential scrambling** — `scramble_credentials` is always called after testing + +### Manual Workflow (Legacy) + +When not using plugins, follow this sequence: + +1. **Research Phase (PARALLEL)** -> Launch `discover_actions` subagent + main agent for auth/docs/external repos +2. **Synchronization** -> Collect and integrate subagent results +3. **Version Validation** -> `analyze_versioning()` -> Detect/resolve API version conflicts +4. **Config Building** -> Create comprehensive YAML with all discovered operations +5. **YAML Validation** -> `npx @stackone/cli validate src/configs//.connector.s1.yaml` +6. **Coverage Validation** -> Confirm adequate endpoint coverage +7. **Action Tracking Setup** -> **MANDATORY** - Save action inventory to `/tmp/_actions_tracking.json` +8. **Testing Phase** -> `test_actions()` -> Test EVERY operation with real API calls for EVERY auth type +9. **Test Completion Verification** -> Verify 100% coverage +10. **Security** -> `scramble_credentials()` -> Secure all credentials +11. **Meta Feedback** -> `meta_feedback()` -> **MANDATORY** - Send feedback for tracking **Skip/Disorder = Incomplete Task** @@ -372,35 +405,35 @@ Follow this **exact sequence** when building Falcon API configurations: ### Quick Reference Workflow -**Step 0:** Reference existing connector → `src/configs/` (same category/auth type) -**Step 1:** StackOne context → `get_stackone_categories()`, `get_stackone_actions(category)` -**Step 2:** Action discovery → `discover_actions()` (PRIMARY - autonomous subagent) -**Step 3:** Auth research → `vector_search()`, `web_search()` -**Step 4:** Documentation → `get_provider_coverage()`, `fetch()`, `extract_oas_actions()` -**Step 5:** External repos → `get_external_integrations()`, `scan_external_repo()` -**Step 6:** Parallel execution → Synchronize all results +**Step 0:** Reference existing connector -> `src/configs/` (same category/auth type) +**Step 1:** StackOne context -> `get_stackone_categories()`, `get_stackone_actions(category)` +**Step 2:** Action discovery -> `discover_actions()` (PRIMARY - autonomous subagent) +**Step 3:** Auth research -> `vector_search()`, `web_search()` +**Step 4:** Documentation -> `get_provider_coverage()`, `fetch()`, `extract_oas_actions()` +**Step 5:** External repos -> `get_external_integrations()`, `scan_external_repo()` +**Step 6:** Parallel execution -> Synchronize all results ### Action Discovery (PRIMARY) **Check S3 first:** ```typescript -map_provider_key("provider_name") → Get exact provider key -get_provider_actions("provider_key") → Check for indexed data +map_provider_key("provider_name") // Get exact provider key +get_provider_actions("provider_key") // Check for indexed data ``` **Launch autonomous discovery if no data exists:** ```typescript // Launch (returns immediately) discover_actions({ provider: "provider_name", maxIterations: 30 }) -→ { taskId: "rpc_xxx", ... } +// -> { taskId: "rpc_xxx", ... } // Poll every 60-90 seconds get_discover_actions_task_status(taskId, provider) -→ Status: "pending" → "running" → "complete" +// Status: "pending" -> "running" -> "complete" // Extract results (5-15 minutes) -→ Result: JSON report with ~100 discovered actions -→ Auto-saved to S3 for future use +// Result: JSON report with ~100 discovered actions +// Auto-saved to S3 for future use ``` **Benefits:** Autonomous (20+ tool calls), Comprehensive, Async (5-15 min), Persistent @@ -415,11 +448,11 @@ const endpoints = discoveredActions.map(a => a.endpoints[0]); // Launch versioning analysis (2-5 minutes) analyze_versioning({ provider: "provider_name", endpoints, maxIterations: 5 }) -→ { taskId: "rpc_xxx", ... } +// -> { taskId: "rpc_xxx", ... } // Poll for status get_analyze_versioning_task_status(taskId, provider) -→ Result: Version analysis with conflicts, migrations, recommendations +// Result: Version analysis with conflicts, migrations, recommendations ``` **Checklist:** @@ -431,7 +464,7 @@ get_analyze_versioning_task_status(taskId, provider) ### Parallel Execution Strategy -1. **Minute 0:** Launch `discover_actions(provider)` → Get taskId +1. **Minute 0:** Launch `discover_actions(provider)` -> Get taskId 2. **Minutes 0-5:** Complete Steps 0-5 (reference, context, auth, docs, repos) 3. **Minutes 5-15:** Poll `get_discover_actions_task_status()` every 60-90 seconds 4. **Minute 15:** Synchronize results @@ -444,23 +477,22 @@ get_analyze_versioning_task_status(taskId, provider) ### Prerequisites & Guidelines -**Important:** - **Default to non-unified actions** unless explicitly told otherwise - Non-unified: Map exactly to provider's API, output provider's response entirely - Unified: Use StackOne schema - only use when explicitly requested - **Ignore deprecated actions, fields, and inputs** - **YAML Best Practice:** Never use `:` as a literal value (use parentheses instead) - - ✅ `description: Filter by status (pending, approved)` - - ❌ `description: Filter by status: pending` + - GOOD: `description: Filter by status (pending, approved)` + - BAD: `description: Filter by status: pending` ### File Structure (ALWAYS Use Partials) -**⚠️ ALWAYS use the partials approach - never create monolithic connector files.** +ALWAYS use the partials approach - never create monolithic connector files. ``` src/configs/{provider}/ -├── {provider}.connector.s1.yaml # Main: info, auth, $refs only -└── {provider}.{resource}.s1.partial.yaml # Actions grouped by resource + {provider}.connector.s1.yaml # Main: info, auth, $refs only + {provider}.{resource}.s1.partial.yaml # Actions grouped by resource ``` - Folder names: **lowercase, no hyphens** (e.g., `smartrecruiters/`) @@ -469,12 +501,12 @@ src/configs/{provider}/ **Example:** ``` src/configs/clickup/ -├── clickup.connector.s1.yaml # Main file -├── clickup.tasks.s1.partial.yaml # Task actions -└── clickup.users.s1.partial.yaml # User actions + clickup.connector.s1.yaml # Main file + clickup.tasks.s1.partial.yaml # Task actions + clickup.users.s1.partial.yaml # User actions ``` -## Key Rules +### Key Rules - **camelCase** for ALL config field names (`scopeDefinitions`, NOT `scope_definitions`) - Never use `:` in YAML string values — use parentheses or rephrase instead @@ -486,7 +518,7 @@ src/configs/clickup/ - `response:` block: unified actions ONLY (omit for non-unified) - `context` field was renamed to `resources` (v2.2.0 breaking change) -## Authentication +### Authentication Only two auth types exist in Falcon: @@ -496,9 +528,9 @@ Only two auth types exist in Falcon: Analyze the provider's ACTUAL auth flow, not their marketing terminology. See plugin `references/auth-patterns.md` for detailed patterns and examples. **Decision Guide:** -- Token exchange via endpoint call? → `oauth2` -- No token exchange? → `custom` -- Custom headers (not Authorization)? → `authorization.type: none` + define headers in action `args` +- Token exchange via endpoint call? -> `oauth2` +- No token exchange? -> `custom` +- Custom headers (not Authorization)? -> `authorization.type: none` + define headers in action `args` **Field Types:** - `setupFields`: T1-facing (OAuth apps, multi-tenant credentials) e.g., Client ID, Client Secret, scopes @@ -542,7 +574,7 @@ authentication: #### OAuth 2.0 Examples -**Typical OAuth:** `asana`, `gmail`, `xero` (Authorization code → Access Token → Refresh Token) +**Typical OAuth:** `asana`, `gmail`, `xero` (Authorization code -> Access Token -> Refresh Token) **Complex:** `jira` (includes post-auth call) **Client Credentials:** `greenhouse`, `globalizationpartners`, `bigchange` @@ -589,9 +621,9 @@ scopeDefinitions: ### Inputs -**⚠️ For non-unified actions: inputs must match exactly the provider's request parameters.** -**DO NOT CREATE INPUTS THAT DO NOT EXIST IN THE PROVIDER API.** -**Ignore deprecated actions/fields/inputs.** +For non-unified actions: inputs must match exactly the provider's request parameters. +DO NOT CREATE INPUTS THAT DO NOT EXIST IN THE PROVIDER API. +Ignore deprecated actions/fields/inputs. Reference with JSONPath: `$.inputs.fieldName` (preferred) or JEXL `'{{inputs.fieldName}}'` for conditional logic/string construction. @@ -666,8 +698,8 @@ fieldConfigs: ### Steps -**⚠️ Every step must have a `description` field.** -**⚠️ Custom authentication headers must be in `args` of every action's request step.** +Every step must have a `description` field. +Custom authentication headers must be in `args` of every action's request step. ```yaml steps: @@ -696,7 +728,7 @@ Defined in `packages/core/src/stepFunctions/stepFunctionsList.ts`. **Always use `args` for parameters (never direct `body` field).** -**⚠️ IMPORTANT:** +**IMPORTANT:** - **For `value` fields**: Use JSONPath `value: $.inputs.fieldName` - **For `condition` fields**: Use JEXL `condition: "{{present(inputs.fieldName)}}"` - **Never use JEXL `'{{inputs.fieldName}}'` for `value` fields** @@ -810,7 +842,7 @@ For embedding dynamic values within strings: **ONLY USE JEXL EXPRESSIONS DEFINED IN THE EXPRESSIONS PACKAGE.** -📖 **Full reference (operators, functions, examples):** `node_modules/@stackone/expressions/README.md` +Full reference (operators, functions, examples): `node_modules/@stackone/expressions/README.md` For conditional logic, transformations, complex expressions (wrap in single quotes): @@ -881,7 +913,7 @@ steps: #### Nested Objects in Queries -**⚠️ IMPORTANT: When querying nested objects, ONLY return the `id` field if a separate action exists to fetch the full object.** +**IMPORTANT: When querying nested objects, ONLY return the `id` field if a separate action exists to fetch the full object.** Applies to both GraphQL and REST APIs. @@ -959,8 +991,7 @@ result: ### YAML Validation (MANDATORY) ```bash -npm install -g @stackone/cli -stackone validate [pathToYaml] +npx @stackone/cli validate src/configs//.connector.s1.yaml ``` Config MUST pass validation before proceeding to testing. @@ -970,11 +1001,10 @@ Config MUST pass validation before proceeding to testing. 2. Similar connectors (same auth type or structure) 3. Provider API documentation 4. Working actions in same connector -5. `connectors/DEVELOPERS.md` ### Action Tracking (MANDATORY) -Before testing begins, create `/tmp/_actions_tracking.json` with complete action inventory including all auth types. Formula: `total_required_tests = operations × auth_types`. +Before testing begins, create `/tmp/_actions_tracking.json` with complete action inventory including all auth types. Formula: `total_required_tests = operations x auth_types`. ### Testing Phase @@ -985,15 +1015,16 @@ Before testing begins, create `/tmp/_actions_tracking.json` with compl **You MUST test every operation with EVERY auth type. Partial testing is NOT acceptable.** **Testing Cycles (Dependency Order):** -1. LIST (no dependencies) → Capture IDs +1. LIST (no dependencies) -> Capture IDs 2. GET (use IDs from LIST) 3. CREATE (generate new resources) — Full mode only 4. UPDATE (use IDs from CREATE) — Full mode only 5. DELETE (clean up from CREATE) — Full mode only **Testing Methods:** -- **Async Tool**: `test_actions()` → poll `get_test_actions_task_status()` (best for batch testing) -- **Manual CLI**: `stackone run --connector --account --credentials --action-id [--params ] [--debug]` +- **Plugin**: `/test-connector` runs live tests with automatic cleanup and credential scrambling +- **Async MCP Tool**: `test_actions()` -> poll `get_test_actions_task_status()` (best for batch testing) +- **Manual CLI**: `npx @stackone/cli test [--debug]` **Error Fix Strategy:** - **400:** Fix parameter structure/type/location @@ -1015,7 +1046,9 @@ Before proceeding to security: ### Credential Scrambling (MANDATORY) -After successful testing, call `scramble_credentials()` with `securityLevel: "PRODUCTION"`. Save ONLY scrambled versions. Delete originals. +When using plugins, credential scrambling is **automatic** — `/test-connector` calls `scramble_credentials` after testing completes. + +When working manually, call `scramble_credentials()` with `securityLevel: "PRODUCTION"` after successful testing. Save ONLY scrambled versions. Delete originals. **Never:** - Commit unscrambled configs to git @@ -1049,7 +1082,7 @@ Call `meta_feedback()` after EVERY config generation, regardless of user prefere ### Testing - `test_actions` / `get_test_actions_task_status` — Run connector action tests (async batch) -- **Manual CLI**: `stackone run --connector --account --credentials --action-id [--params ] [--debug]` +- **Manual CLI**: `npx @stackone/cli test [--debug]` ### Description Improvement - `improve_descriptions` / `get_improve_descriptions_task_status` — Improve YAML descriptions (async) @@ -1058,17 +1091,21 @@ Call `meta_feedback()` after EVERY config generation, regardless of user prefere - `scramble_credentials` — Scramble stored credentials after use (MANDATORY) - `meta_feedback` — Submit feedback for tracking (MANDATORY) -### CLI Validation -- `stackone validate ` — Validate YAML syntax and structure +### CLI +- `npx @stackone/cli validate ` — Validate YAML syntax and structure +- `npx @stackone/cli test ` — Test a single action live +- `npx @stackone/cli pull ` — Pull existing connector from StackOne index +- `npx @stackone/cli scaffold ` — Create blank connector template +- `npx @stackone/cli list` — List available connectors in StackOne index ## Boundaries **Always:** -- Follow the 11-step Critical Workflow in exact order -- Create action tracking file before testing +- Use plugin workflows when available (`/build-connector` or `/build-unified-connector`) +- When working manually, follow the 11-step workflow in exact order - Test every operation with EVERY auth type - Validate YAML before testing -- Scramble credentials before storage +- Scramble credentials before storage (automatic via plugins, manual otherwise) - Send meta feedback **Ask First:** @@ -1078,11 +1115,10 @@ Call `meta_feedback()` after EVERY config generation, regardless of user prefere - Proceeding with partial test coverage **Never:** -- Skip action tracking file creation - Skip testing any auth type -- Proceed without 100% test coverage +- Proceed without full test coverage - Skip `discover_actions` for research -- Skip `scramble_credentials` +- Skip `scramble_credentials` (unless using plugins, which do it automatically) - Skip `meta_feedback` - Commit plaintext credentials - Ignore validation errors From 01f81a0537bdc7343db7e379326c2fbc0a0173f7 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 11:22:49 +0000 Subject: [PATCH 07/13] fix: Addressing all comments. --- .../references/actions-and-steps.md | 3 +- .../references/auth-patterns.md | 58 +- .../references/cli-commands.md | 6 +- .../references/connector-patterns.md | 2 +- .../skills/build-config/SKILL.md | 141 +- .../skills/configure-auth/SKILL.md | 4 +- .../skills/discover-actions/SKILL.md | 4 +- .../skills/setup-connector/SKILL.md | 8 +- .../skills/test-connector/SKILL.md | 4 +- .../skills/validate-connector/SKILL.md | 24 +- .../references/connector-patterns.md | 2 +- .../references/field-mapping-guide.md | 11 +- .../references/schemas/accounting.md | 4 +- .../references/schemas/hris.md | 2 + .../references/schemas/lms.md | 2 + .../skills/choose-schema/SKILL.md | 8 +- .../skills/import-schema/SKILL.md | 7 +- .../skills/map-fields/SKILL.md | 8 +- .../skills/scope-actions/SKILL.md | 8 +- .../skills/test-connector/SKILL.md | 3 +- .../skills/validate-connector/SKILL.md | 1 + .claude/settings.local.json | 29 + ...onnector-builder-plugins-implementation.md | 1577 +++++++++++++++++ ...3-20-connector-builder-plugins-redesign.md | 128 ++ .../ashby/ashby.candidates.s1.partial.yaml | 6 + .../bamboo-hr/bamboo-hr.connector.s1.yaml | 1 + .../bamboo-hr.employees.s1.partial.yaml | 24 +- .../breathehr/breathehr_v1-0-1.s1.yaml | 3 +- 28 files changed, 1978 insertions(+), 100 deletions(-) create mode 100644 docs/plans/2026-03-20-connector-builder-plugins-implementation.md create mode 100644 docs/plans/2026-03-20-connector-builder-plugins-redesign.md diff --git a/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md b/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md index 4f58396..cfb54d8 100644 --- a/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md +++ b/.claude/plugins/stackone-connector-builder/references/actions-and-steps.md @@ -109,9 +109,8 @@ For conditional logic, transformations. **Wrap in single quotes.** - String manipulation: `'{{inputs.name.toUpperCase()}}'` **IMPORTANT:** -- For `value` fields: use JSONPath `$.inputs.fieldName` +- For `value` fields: prefer JSONPath `$.inputs.fieldName` for simple references. JEXL `'{{...}}'` is also supported in `value` fields when you need transformations, conditionals, or string manipulation. - For `condition` fields: use JEXL `'{{present(inputs.fieldName)}}'` -- **Never use JEXL for `value` fields** ## Step Functions diff --git a/.claude/plugins/stackone-connector-builder/references/auth-patterns.md b/.claude/plugins/stackone-connector-builder/references/auth-patterns.md index fe6c04b..9830bf4 100644 --- a/.claude/plugins/stackone-connector-builder/references/auth-patterns.md +++ b/.claude/plugins/stackone-connector-builder/references/auth-patterns.md @@ -160,33 +160,71 @@ authentication: - oauth2: type: oauth2 label: OAuth 2.0 - grantType: authorization_code - authorizationUrl: https://api.provider.com/oauth/authorize - tokenUrl: https://api.provider.com/oauth/token setupFields: - key: clientId label: Client ID - type: string + type: text required: true - key: clientSecret label: Client Secret type: password required: true secret: true - scopes: - - resource.read - - resource.write + - key: scopes + label: Application Scopes + type: text + required: false + authorization: + type: oauth2 + authorizationUrl: https://api.provider.com/oauth/authorize + authorizationParams: + response_type: code + client_id: $.credentials.clientId + redirect_uri: ${apiHostUri}/connect/oauth2/provider/callback + scope: $.credentials.scopes + tokenUrl: https://api.provider.com/oauth/token + token: $.credentials.accessToken + includeBearer: true testActions: - action: get_current_user required: true ``` -If the refresh token request does not require authorization headers, add: +If the refresh token request does not require authorization headers, add `authorization: type: none` on the refresh request **step** (not at the top level): ```yaml refreshAuthentication: - authorization: - type: none + action: + actionId: refresh_token_provider + categories: + - internal + actionType: refresh_token + label: Refresh Token + description: Refresh OAuth2 token + steps: + - stepId: refresh_token_request + description: Get new access token + stepFunction: + functionName: request + parameters: + baseUrl: https://api.provider.com + url: /oauth/token + method: post + authorization: + type: none + args: + - name: grant_type + value: refresh_token + in: body + - name: refresh_token + value: $.credentials.refreshToken + in: body + - name: client_id + value: $.credentials.clientId + in: body + - name: client_secret + value: $.credentials.clientSecret + in: body ``` --- diff --git a/.claude/plugins/stackone-connector-builder/references/cli-commands.md b/.claude/plugins/stackone-connector-builder/references/cli-commands.md index 72ac83b..a465b53 100644 --- a/.claude/plugins/stackone-connector-builder/references/cli-commands.md +++ b/.claude/plugins/stackone-connector-builder/references/cli-commands.md @@ -66,9 +66,9 @@ WORKDAY_BASE_URL=https://api.workday.com | `YAML parse error at line N` | Invalid YAML syntax | Check indentation — use 2 spaces, not tabs | | `Missing required field: entrypointUrl` | Action missing URL | Add `entrypointUrl` to the action | | `Unknown actionType: custom2` | Typo in actionType | Valid: `custom`, `list`, `get`, `create`, `update`, `delete` | -| `schemaType required for unified action` | Missing schemaType | Add `schemaType: unified` to the action | -| `fieldConfigs required when schemaType is unified` | Missing field mapping | Add `fieldConfigs` array to the action | -| `Missing step: map_fields` | Unified action missing pipeline step | Add all three steps: `request`, `map_fields`, `typecast` | +| `schemaType required for unified action` | Missing schemaType (unified actions only) | Add `schemaType: unified` — only needed for unified actions, not `actionType: custom` | +| `fieldConfigs required when schemaType is unified` | Missing field mapping (unified actions only) | Add `fieldConfigs` array — only applicable to unified actions | +| `Missing step: map_fields` | Unified action missing pipeline step (unified actions only) | Add `request`, `map_fields`, `typecast` steps — only for unified actions | | `401 Unauthorized` | Bad credentials | Check `.env` and the `authentication` block in connector YAML | | `404 Not Found` | Wrong endpoint URL | Verify `entrypointUrl` against provider API docs | | `Cannot find module '@stackone/cli'` | CLI not installed | Run `npm install -g @stackone/cli` or use `npx` | diff --git a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md index e484082..4de1dd8 100644 --- a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md +++ b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md @@ -535,4 +535,4 @@ result: | Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | | Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | -Reference inside the connector using `${args..value}` after defining in the authentication `args` block. +Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation), where `` matches a `configFields` or `setupFields` key in the authentication block. diff --git a/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md index 8fef192..70f01f0 100644 --- a/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md @@ -35,83 +35,140 @@ Create `src/configs/{{provider}}/{{provider}}.{{resource}}.s1.partial.yaml` for ### list action ```yaml -- name: list_{{resource}} +- actionId: list_{{resource}} + categories: + - {{category}} actionType: custom - entrypointUrl: "/{{resource}}" - entrypointHttpMethod: GET - inputs: - page: - type: number - required: false - per_page: - type: number - required: false + label: List {{Resource}} + description: List all {{resource}} steps: - - type: request - id: fetch_{{resource}} + - stepId: fetch_{{resource}} + description: Retrieve all {{resource}} + stepFunction: + functionName: request + parameters: + url: '/{{resource}}' + method: get + result: + data: $.steps.fetch_{{resource}}.output.data ``` ### get action ```yaml -- name: get_{{resource}} +- actionId: get_{{resource}} + categories: + - {{category}} actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: GET + label: Get {{Resource}} + description: Get a specific {{resource}} by ID inputs: - id: + - name: id + description: {{Resource}} ID type: string + in: path required: true steps: - - type: request - id: fetch_{{resource}} + - stepId: fetch_{{resource}} + description: Retrieve {{resource}} by ID + stepFunction: + functionName: request + parameters: + url: '/{{resource}}/${inputs.id}' + method: get + result: + data: $.steps.fetch_{{resource}}.output.data ``` ### create action ```yaml -- name: create_{{resource}} +- actionId: create_{{resource}} + categories: + - {{category}} actionType: custom - entrypointUrl: "/{{resource}}" - entrypointHttpMethod: POST + label: Create {{Resource}} + description: Create a new {{resource}} inputs: - body: - type: object + - name: {{field_name}} + description: {{field_description}} + type: string + in: body required: true steps: - - type: request - id: create_{{resource}} + - stepId: create_{{resource}} + description: Create a new {{resource}} + stepFunction: + functionName: request + parameters: + url: '/{{resource}}' + method: post + args: + - name: {{field_name}} + value: $.inputs.{{field_name}} + in: body + result: + data: $.steps.create_{{resource}}.output.data ``` ### update action ```yaml -- name: update_{{resource}} +- actionId: update_{{resource}} + categories: + - {{category}} actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: PATCH + label: Update {{Resource}} + description: Update an existing {{resource}} inputs: - id: + - name: id + description: {{Resource}} ID type: string + in: path required: true - body: - type: object - required: true + - name: {{field_name}} + description: {{field_description}} + type: string + in: body + required: false steps: - - type: request - id: update_{{resource}} + - stepId: update_{{resource}} + description: Update {{resource}} + stepFunction: + functionName: request + parameters: + url: '/{{resource}}/${inputs.id}' + method: patch + args: + - name: {{field_name}} + value: $.inputs.{{field_name}} + in: body + condition: '{{present(inputs.{{field_name}})}}' + result: + data: $.steps.update_{{resource}}.output.data ``` ### delete action ```yaml -- name: delete_{{resource}} +- actionId: delete_{{resource}} + categories: + - {{category}} actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: DELETE + label: Delete {{Resource}} + description: Delete a {{resource}} by ID inputs: - id: + - name: id + description: {{Resource}} ID type: string + in: path required: true steps: - - type: request - id: delete_{{resource}} + - stepId: delete_{{resource}} + description: Delete {{resource}} + stepFunction: + functionName: request + parameters: + url: '/{{resource}}/${inputs.id}' + method: delete + result: + data: $.steps.delete_{{resource}}.output.data ``` --- @@ -122,8 +179,8 @@ Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` and add a `$ref` ```yaml actions: - - $ref: "./{{provider}}.employees.s1.partial.yaml" - - $ref: "./{{provider}}.departments.s1.partial.yaml" + $ref: {{provider}}.employees + $ref: {{provider}}.departments ``` --- diff --git a/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md index bba605b..8551790 100644 --- a/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md @@ -74,7 +74,9 @@ Show the written auth block and ask: Apply any corrections. -Save `auth_type` to session. +Save to session: +- `auth_type`: The authentication type (`custom` or `oauth2`) +- `auth_details`: Full auth configuration including header names, credential references, and authorization type (e.g., `{ "authorization_type": "none", "custom_headers": [{ "name": "X-Api-Key", "value": "$.credentials.apiKey" }] }`). This is needed by `/build-config` to add the correct auth headers to each action's request step. --- diff --git a/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md index 3d8d93f..888afc4 100644 --- a/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md @@ -144,10 +144,12 @@ Poll with `get_analyze_versioning_task_status`. Identifies deprecated endpoints ### B6: Save to session +Save only the actions the builder explicitly selected — not all discovered actions: + ```json { "discovery_mode": "maximal", - "action_scope": { "...all confirmed actions..." }, + "action_scope": { "...only builder-selected actions..." }, "use_case": "Full API coverage" } ``` diff --git a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md index 5e7fdce..69f7bdd 100644 --- a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md @@ -59,12 +59,18 @@ If unavailable: ## Step 4: Pull or scaffold -**If CLI available and provider exists in StackOne index:** +**If CLI available, first check if a pullable connector exists:** +```bash +npx @stackone/cli list | grep -i {{provider}} +``` +If found in the CLI list, pull it: ```bash npx @stackone/cli pull {{provider}} ``` Verify `src/configs/{{provider}}/` was created. Save `connector_exists: true`. +Note: A provider may have indexed actions (from Step 2) but no pullable connector config. Always check the CLI list before attempting `pull`. + **If not in index or scaffolding fresh:** ```bash npx @stackone/cli scaffold {{provider}} diff --git a/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md index f9583d0..cc9b869 100644 --- a/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md @@ -134,10 +134,10 @@ Write the updated `test_artifacts` array back to `.connector-build-session.json` ### Deleting Test Records -After the `update` step for a resource is verified, run `delete` on the test record created during `create`: +After the last mutating step for a resource is verified (e.g., `update`, or `create` if no `update` exists), run `delete` on the test record created during `create`: - If `delete_{{resource}}` is in `action_scope`: run it targeting the created record's ID -- After delete: run `get_{{resource}}` for that ID and confirm a 404 or empty response +- After delete: if `get_{{resource}}` is in `action_scope`, run it for that ID and confirm a 404 or empty response - Mark the artifact as `cleaned_up: true` in the session file ### When Delete Is Not Available diff --git a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md index 96be2aa..67fcb5c 100644 --- a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md @@ -41,13 +41,13 @@ Work through each action across all `{{provider}}.{{resource}}.s1.partial.yaml` 1. **`actionType: custom`** — Every action must have `actionType: custom`. No other value is valid for generic connectors. -2. **`entrypointUrl` present** — Every action must have an `entrypointUrl`. It must be a string starting with `/` or a full URL. Template expressions like `${inputs.id}` are allowed. +2. **`entrypointUrl` present (non-custom only)** — Actions with `actionType` other than `custom` must have an `entrypointUrl`. It must be a string starting with `/` or a full URL. Template expressions like `${inputs.id}` are allowed. Not required for `actionType: custom`. -3. **`entrypointHttpMethod` present** — Every action must have `entrypointHttpMethod`. Valid values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. +3. **`entrypointHttpMethod` present (non-custom only)** — Actions with `actionType` other than `custom` must have `entrypointHttpMethod`. Valid values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. Not required for `actionType: custom`. 4. **At least one `type: request` step** — Every action must have a `steps` array with at least one entry where `type: request`. Generic actions have exactly one step. -5. **`list` actions have pagination inputs** — Actions named `list_*` must have at least one pagination input (`page`, `per_page`, `offset`, `limit`, or provider-specific equivalent) with `required: false`. +5. **`list` actions have pagination inputs (if supported)** — Actions named `list_*` should have pagination inputs (`page`, `per_page`, `offset`, `limit`, or provider-specific equivalent) with `required: false`, if the provider endpoint supports pagination. 6. **`get` actions reference `${inputs.id}` in URL** — Actions named `get_*` must have `${inputs.id}` in `entrypointUrl` and an `id` input with `required: true`. @@ -77,10 +77,22 @@ Cause: The action YAML block is missing the `entrypointUrl` key. Fix — add the field: ```yaml -- name: list_employees +- actionId: list_employees + categories: + - {{category}} actionType: custom - entrypointUrl: "/employees" # <-- add this - entrypointHttpMethod: GET + label: List Employees + description: List all employees + steps: + - stepId: fetch_employees + description: Retrieve all employees + stepFunction: + functionName: request + parameters: + url: '/employees' + method: get + result: + data: $.steps.fetch_employees.output.data ``` --- diff --git a/.claude/plugins/stackone-unified-builder/references/connector-patterns.md b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md index e484082..4de1dd8 100644 --- a/.claude/plugins/stackone-unified-builder/references/connector-patterns.md +++ b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md @@ -535,4 +535,4 @@ result: | Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | | Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | -Reference inside the connector using `${args..value}` after defining in the authentication `args` block. +Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation), where `` matches a `configFields` or `setupFields` key in the authentication block. diff --git a/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md index 3053366..4e98c83 100644 --- a/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md +++ b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md @@ -85,12 +85,19 @@ Translate provider-specific values to your schema's enum values: **Important:** Keys are case-sensitive and must match the provider's exact values. -For complex logic, use `matchExpression` (JEXL): +For complex logic, use `enumMapper.matcher[]` with `matchExpression` (JEXL): ```yaml - targetFieldKey: gender expression: "$.gender" + type: enum enumMapper: - matchExpression: "'{{$.gender === \"M\" ? \"male\" : $.gender === \"F\" ? \"female\" : \"other\"}}'" + matcher: + - matchExpression: '{{$.gender == "M"}}' + value: male + - matchExpression: '{{$.gender == "F"}}' + value: female + - matchExpression: '{{$.gender != "M" && $.gender != "F"}}' + value: other ``` --- diff --git a/.claude/plugins/stackone-unified-builder/references/schemas/accounting.md b/.claude/plugins/stackone-unified-builder/references/schemas/accounting.md index 060661a..499a64b 100644 --- a/.claude/plugins/stackone-unified-builder/references/schemas/accounting.md +++ b/.claude/plugins/stackone-unified-builder/references/schemas/accounting.md @@ -77,7 +77,7 @@ |-------|------|-------------| | `account_id` | `string` | ID of the ledger account this line references | | `description` | `string` | Description of the journal line | -| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negativ... | +| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negative for credit. | | `tax_rate_id` | `string` | ID of the tax rate applied | | `tax_amount` | `number` | Tax amount of the line in minor units, e.g. 10010 for 100.10 USD | @@ -90,7 +90,7 @@ | `description` | `string` | Description of the journal line | | `currency_code` | `CurrencyEnum` | Currency code for this line | | `exchange_rate` | `number` | Exchange rate to company base currency | -| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negativ... | +| `amount` | `number` | Amount of the journal line in minor units, e.g. 10010 for 100.10 USD. Positive for debit, negative for credit. | | `tax_amount` | `number` | Tax amount of the line in minor units, e.g. 10010 for 100.10 USD | | `tax_rate_percentage` | `number` | Tax rate percentage | diff --git a/.claude/plugins/stackone-unified-builder/references/schemas/hris.md b/.claude/plugins/stackone-unified-builder/references/schemas/hris.md index 97956d0..ca374fd 100644 --- a/.claude/plugins/stackone-unified-builder/references/schemas/hris.md +++ b/.claude/plugins/stackone-unified-builder/references/schemas/hris.md @@ -800,6 +800,8 @@ ## Enums +> **Note:** In API responses, enum fields are returned as objects with `value` (the unified enum value from the table below) and `source_value` (the raw provider value). For example: `{ "value": "active", "source_value": "Active" }`. + | Enum | Values | |------|--------| | `BankAccountTypeEnum` | `savings`, `checking`, `current`, `business`, `personal`, `other` | diff --git a/.claude/plugins/stackone-unified-builder/references/schemas/lms.md b/.claude/plugins/stackone-unified-builder/references/schemas/lms.md index 1133e8d..85b623e 100644 --- a/.claude/plugins/stackone-unified-builder/references/schemas/lms.md +++ b/.claude/plugins/stackone-unified-builder/references/schemas/lms.md @@ -252,6 +252,8 @@ ## Enums +> **Note:** In API responses, enum fields are returned as objects with `value` (the unified enum value from the table below) and `source_value` (the raw provider value). For example: `{ "value": "pending", "source_value": "Pending" }`. + | Enum | Values | |------|--------| | `AssignmentStatusEnum` | `pending`, `in_progress`, `completed` | diff --git a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md index a58d032..785fcee 100644 --- a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md @@ -12,8 +12,10 @@ Step 1 of the unified connector build process. Read `.connector-build-session.json` from the project root if it exists. -If it exists and `schema` is already set, greet the builder: -> "Resuming session — schema already set to `{{schema}}` ({{schema_source}}). Run `/check-connector` to continue, or type `reset` to start over." +If it exists and both `schema` and `provider` are already set, greet the builder: +> "Resuming session — schema already set to `{{schema}}` ({{schema_source}}) for provider `{{provider}}`. Run `/check-connector` to continue, or type `reset` to start over." + +If it exists and `schema` is set but `provider` is missing (e.g., from an imported schema), proceed to Step 1 to collect the provider name. If it does not exist or `schema` is empty, proceed below. @@ -67,7 +69,7 @@ Read the schema file. Use the **Endpoints** section to show the builder what API Ask: > "Which resource(s) do you need to expose? Here's what's available in `{{category}}`:" -List the model names from the schema file (e.g., `Employee`, `Employment`, `HRISDepartment`). +List the available resources from the schema file (e.g., `Employee`, `Employment`, `HRISDepartment`). When saving to session, convert model names to lowercase resource slugs (e.g., `Employee` → `employees`, `HRISDepartment` → `departments`) since later steps use slugs for action names and file paths. Once resources are chosen, show the fields table for each from the loaded schema file. diff --git a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md index 3de2cae..579184a 100644 --- a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md @@ -169,8 +169,9 @@ Once confirmed, save to `.connector-build-session.json`: > > These fields will be used as your `targetFieldKey` names in `/map-fields`. The next step is deciding which provider operations to build. > -> - If you haven't set a provider yet: run `/choose-schema` and choose **option B** (it'll pick up your imported schema) -> - If provider + actions are already set: run `/map-fields` directly" +> - If you haven't set a provider yet: tell me which provider you're building for, then run `/check-connector` +> - If provider is already set but connector hasn't been pulled/scaffolded: run `/check-connector` +> - If provider + connector + actions are already set: run `/map-fields` directly" Update session: -- `session_step` → `"scope-actions"` (if provider is set) or `"choose-schema"` (if not) +- `session_step` → `"check-connector"` (if provider is set but connector not yet pulled/scaffolded) or `"choose-schema"` (if provider is not set — the choose-schema flow will collect the provider name first since `provider` is missing) diff --git a/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md index d9b41f8..ddfa0a5 100644 --- a/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md @@ -46,12 +46,14 @@ Use any matching results to inform the fieldConfigs you build. Getting a real sample response makes field mapping accurate. -**If `cli_available` is true in session:** +**If `cli_available` is true in session and the action already exists in the connector YAML:** ```bash npx @stackone/cli test {{provider}} list_{{resource}} --debug ``` This shows the raw JSON before any mapping. Use the output to identify exact field paths. +**If the action does not exist yet** (e.g., new connector with no actions built): Skip the CLI test for now — the action will be created in Step 3. Instead, use `vector_search` or `web_search` to find example API responses for the provider's endpoint, or ask the builder to provide one. + **If CLI is not available:** Ask: > "Could you paste an example response from `{{provider}}`'s `{{resource}}` API endpoint? Even a partial response with one or two records is enough. This helps me map the fields accurately." @@ -61,7 +63,7 @@ This shows the raw JSON before any mapping. Use the output to identify exact fie Work through resources one at a time. -For each schema field (from `schema_fields` in session or the built-in schema from `unified-schemas.md`): +For each schema field (from `schema_fields` in session, or — for built-in schemas — from the selected `schema_file` at `${CLAUDE_PLUGIN_ROOT}/{{schema_file}}`): 1. Show the field: e.g., `first_name (string, required)` 2. Ask: "What is the field path in the `{{provider}}` response? (e.g., `$.firstName`, `$.contact.first_name`)" @@ -94,7 +96,7 @@ For each resource, create or update: Use the unified action pattern from `connector-patterns.md`. For each action in scope: -- `name: unified_{{action}}_{{resource}}` +- `actionId: unified_{{action}}_{{resource}}` - `actionType: {{action}}` - `schemaType: unified` - `schema: {{schema}}/{{resource}}` diff --git a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md index 53de354..49a6482 100644 --- a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md @@ -54,7 +54,13 @@ Save the use case description to session as `use_case`. ## Step 3: Present a recommended scope -Based on the session `resources` and the use case, recommend an action set. +First, check if indexed provider actions are available: +``` +get_provider_actions("{{provider_key}}") +``` +If indexed actions exist, use them to inform the recommendation — only suggest actions the provider's API actually supports. If no indexed data is available, recommend based on the session `resources` and use case, but note that support should be verified during testing. + +Based on the session `resources`, the use case, and any indexed provider actions, recommend an action set. Example recommendation: > "Based on your schema and use case, here's what I recommend: diff --git a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md index 8d1d22f..57eeaf1 100644 --- a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md @@ -46,7 +46,8 @@ Use `test_actions` for automated testing: ``` test_actions({ provider: "{{provider}}", - actions: ["unified_list_{{resource}}", "unified_get_{{resource}}"] + actions: ["unified_list_{{resource}}", "unified_get_{{resource}}", "unified_create_{{resource}}", "unified_update_{{resource}}", "unified_delete_{{resource}}"] + // Include only actions that exist in action_scope }) ``` diff --git a/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md index 514f3e4..3fb87e2 100644 --- a/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/validate-connector/SKILL.md @@ -31,6 +31,7 @@ npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.ya Read each `.yaml` and `.partial.yaml` file in `src/configs/{{provider}}/` and check: - All unified actions have `schemaType: unified` +- All unified actions have `entrypointUrl` and `entrypointHttpMethod` - All unified actions have a `fieldConfigs` array - All unified actions have all three steps: `request`, `map_fields` (version 2), `typecast` (version 2) - `list` actions have a `cursor` block diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5b6382c..376193c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,10 +3,39 @@ "allow": [ "mcp__stackone-agent-global__get_docs", "mcp__stackone-agent-global__get_stackone_categories", + "mcp__stackone-agent-global__get_stackone_actions", "mcp__stackone-agent-global__map_provider_key", + "mcp__stackone-agent-global__get_providers", + "mcp__stackone-agent-global__get_provider_actions", + "mcp__stackone-agent-global__get_provider_coverage", "mcp__stackone-agent-global__web_search", "mcp__stackone-agent-global__fetch", + "mcp__stackone-agent-global__extract_html_text", "mcp__stackone-agent-global__vector_search", + "mcp__stackone-agent-global__discover_actions", + "mcp__stackone-agent-global__get_discover_actions_task_status", + "mcp__stackone-agent-global__analyze_versioning", + "mcp__stackone-agent-global__get_analyze_versioning_task_status", + "mcp__stackone-agent-global__test_actions", + "mcp__stackone-agent-global__get_test_actions_task_status", + "mcp__stackone-agent-global__improve_descriptions", + "mcp__stackone-agent-global__get_improve_descriptions_task_status", + "mcp__stackone-agent-global__scramble_credentials", + "mcp__stackone-agent-global__meta_feedback", + "mcp__stackone-agent-global__get_external_integrations", + "mcp__stackone-agent-global__analyze_external_integration", + "mcp__stackone-agent-global__analyze_external_integrations", + "mcp__stackone-agent-global__get_external_repos", + "mcp__stackone-agent-global__scan_external_repo", + "mcp__stackone-agent-global__search_external_repo", + "mcp__stackone-agent-global__extract_oas_actions", + "mcp__stackone-agent-global__get_stackone_expressions", + "mcp__stackone-agent-global__falcon_workflow", + "mcp__stackone-agent-global__falcon_research", + "mcp__stackone-agent-global__falcon_build", + "mcp__stackone-agent-global__falcon_oauth", + "mcp__stackone-agent-global__falcon_test", + "mcp__stackone-agent-global__falcon_security", "Bash(grep:*)", "Bash(stackone validate:*)" ] diff --git a/docs/plans/2026-03-20-connector-builder-plugins-implementation.md b/docs/plans/2026-03-20-connector-builder-plugins-implementation.md new file mode 100644 index 0000000..84ee6ed --- /dev/null +++ b/docs/plans/2026-03-20-connector-builder-plugins-implementation.md @@ -0,0 +1,1577 @@ +# Connector Builder Plugins Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Rename `stackone-connector-builder` → `stackone-unified-builder` (invoke: `/build-unified-connector`) and create a new `stackone-connector-builder` plugin for generic Falcon connector builds (invoke: `/build-connector`). + +**Architecture:** Both plugins live under `.claude/plugins/` in connectors-template. The rename is a directory move + content updates. The new generic plugin mirrors the unified structure but replaces schema/field-mapping steps with action discovery and raw config building, uses StackOne MCP tools throughout, and includes test cleanup behaviour. + +**Tech Stack:** Claude Code plugin system (SKILL.md format), StackOne MCP tools (`mcp__stackone-agent-global__*`), StackOne CLI (`npx @stackone/cli`), YAML connector configs, JSON session file for state. + +--- + +### Task 1: Rename plugin directory and update plugin.json + +**Files:** +- Rename: `.claude/plugins/stackone-connector-builder/` → `.claude/plugins/stackone-unified-builder/` +- Modify: `.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json` + +**Step 1: Rename the directory** + +```bash +mv /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder \ + /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder +``` + +**Step 2: Update plugin.json** + +Write this content to `.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json`: + +```json +{ + "name": "stackone-unified-builder", + "version": "1.0.0", + "description": "Interactive step-by-step wizard for building unified Falcon connectors. Walks external builders through schema selection, connector scaffolding, action scoping, field mapping, validation, and testing.", + "author": { + "name": "StackOne", + "email": "engineering@stackone.com" + }, + "license": "MIT", + "keywords": ["connector", "falcon", "unified", "schema", "stackone", "builder"] +} +``` + +**Step 3: Verify** +```bash +ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/ +cat /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json +``` +Expected: directory exists, JSON has `"name": "stackone-unified-builder"`. + +--- + +### Task 2: Update unified builder orchestrator skill + +**Files:** +- Modify: `.claude/plugins/stackone-unified-builder/skills/stackone-connector-builder/SKILL.md` +- Rename folder: `skills/stackone-connector-builder/` → `skills/stackone-unified-builder/` + +**Step 1: Rename the skill folder** + +```bash +mv /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-connector-builder \ + /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder +``` + +**Step 2: Write updated SKILL.md** + +Write this content to `.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md`: + +```markdown +--- +name: stackone-unified-builder +description: Interactive wizard for building a unified Falcon connector from scratch. Guides external builders through schema selection, connector setup, action scoping, field mapping, validation, and testing. Auto-triggers when someone asks to build a new unified connector, map provider data to a schema, or integrate with a new provider using StackOne's unified API. +invoke: build-unified-connector +--- + +# Build Unified Connector + +End-to-end wizard for building a unified StackOne Falcon connector. + +## Quick Reference + +Run steps in order, or invoke any sub-skill directly to jump to that phase: + +| Step | Command | What it does | +|------|---------|-------------| +| 1 | `/choose-schema` | Pick your schema: built-in (A), import from file (B), or define inline (C) | +| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or any schema document | +| 2 | `/check-connector` | Check if connector exists, pull or scaffold | +| 3 | `/scope-actions` | Decide which resources and operations to expose | +| 4 | `/map-fields` | Map provider API fields to your schema | +| 5 | `/validate-connector` | Validate the YAML configuration | +| 6 | `/test-connector` | Test live against the provider API | + +Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. + +`/import-schema` can be run standalone at any point to load schema fields from a document. It feeds directly into `/map-fields`. + +--- + +## Starting the Wizard + +Check for an existing `.connector-build-session.json`. If found: +> "Found an existing session: +> - **Provider:** `{{provider}}` +> - **Schema:** `{{schema}}` +> - **Last step completed:** `{{session_step}}` +> +> Would you like to: +> - **Resume** from `{{session_step}}` +> - **Restart** from the beginning (clears session)" + +If no session exists, greet the builder and proceed to Step 1: +> "Welcome to the StackOne unified connector builder. I'll guide you through building a connector that maps provider data to a standardised schema step by step." + +--- + +## Step 1 — Choose Schema + +Execute the full `/choose-schema` skill logic. + +**Outcome saved to session:** +- `provider` — the provider name (e.g., `bamboohr`) +- `schema` — the target category or `custom` +- `schema_source` — `builtin`, `imported`, or `custom` +- `resources` — list of resources to build +- `schema_fields` — field definitions (for custom/imported schemas) +- `schema_file` — path to per-category schema reference (for builtin) + +--- + +## Step 2 — Check Connector + +Execute the full `/check-connector` skill logic. + +**Outcome saved to session:** +- `cli_available` — whether the StackOne CLI is usable +- `connector_exists` — whether a base config was pulled from the index +- `connector_path` — where the config lives +- `auth_type` — the authentication method + +--- + +## Step 3 — Scope Actions + +Execute the full `/scope-actions` skill logic. + +**Outcome saved to session:** +- `action_scope` — `{ resource: [actions] }` map +- `use_case` — builder's description of their goal +- `known_limitations` — any flagged gaps + +--- + +## Step 4 — Map Fields + +Execute the full `/map-fields` skill logic. + +**Outcome:** Partial YAML files written to `src/configs/{{provider}}/` + +--- + +## Step 5 — Validate + +Execute the full `/validate-connector` skill logic. + +**Outcome saved to session:** +- `validated: true` + +--- + +## Step 6 — Test + +Execute the full `/test-connector` skill logic. + +**Outcome saved to session:** +- `tested: true` +- `completed_at` + +--- + +## Session File Schema + +`.connector-build-session.json` lives at the project root and carries all state between steps: + +```json +{ + "provider": "bamboohr", + "schema": "hris", + "schema_source": "builtin", + "schema_file": "references/schemas/hris.md", + "schema_fields": [], + "resources": ["employees", "time_off"], + "cli_available": true, + "connector_exists": false, + "connector_path": "src/configs/bamboohr", + "auth_type": "api_key", + "action_scope": { + "employees": ["list", "get"], + "time_off": ["list", "get", "create"] + }, + "use_case": "Sync HR data into customer's internal system", + "known_limitations": [], + "validated": false, + "tested": false, + "session_step": "map-fields", + "completed_at": null +} +``` + +--- + +## Rules + +- Do not write YAML files until Step 4 (`map-fields`) +- Do not run live tests until Step 5 (`validate-connector`) has passed +- Do not delete the session file unless the builder explicitly requests a reset +- Always read session context at the start of each step — never ask for information already in the session +``` + +**Step 3: Verify** +```bash +head -5 /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md +``` +Expected: frontmatter shows `name: stackone-unified-builder` and `invoke: build-unified-connector`. + +--- + +### Task 3: Add MCP tool references to unified builder sub-skills + +**Files:** +- Modify: `.claude/plugins/stackone-unified-builder/skills/check-connector/SKILL.md` +- Modify: `.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md` +- Modify: `.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md` + +**Step 1: Update check-connector — add S3 index check before CLI** + +In `check-connector/SKILL.md`, after the "Session File" section and before "Step 1: Detect CLI availability", insert: + +```markdown +## Step 0: Check StackOne provider index + +Before checking the CLI, look up the provider in StackOne's index: + +``` +map_provider_key("{{provider}}") +``` + +If a provider key is returned, run: + +``` +get_provider_actions("{{provider_key}}") +``` + +If indexed actions exist, tell the builder: +> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference when building your connector." + +Save the provider key to session as `provider_key`. Continue to CLI check below. +``` + +**Step 2: Update map-fields — add vector_search for field coverage** + +In `map-fields/SKILL.md`, after the "Session File" section, insert: + +```markdown +## Step 0: Check StackOne action coverage + +Before mapping fields, check whether StackOne has coverage data for this provider: + +``` +get_provider_coverage("{{provider_key}}") +``` + +If coverage data exists, use it to pre-populate likely field paths and flag any known gaps. + +Also run a vector search to find similar connectors as reference: + +``` +vector_search("{{schema}} {{provider}} field mapping") +``` + +Use any matching results to inform the fieldConfigs you build. +``` + +**Step 3: Update test-connector — add test_actions and scramble_credentials** + +In `test-connector/SKILL.md`, replace the section "If CLI is available:" for running tests with: + +```markdown +**If CLI is available and MCP tools are accessible:** + +Use `test_actions` for automated testing: +``` +test_actions({ + provider: "{{provider}}", + actions: ["unified_list_{{resource}}", "unified_get_{{resource}}"] +}) +``` + +Poll with `get_test_actions_task_status(taskId)` until complete. + +**If running manually via CLI:** +```bash +npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} +``` + +**After all tests pass, always run:** +``` +scramble_credentials("{{provider}}") +``` +``` + +**Step 4: Verify all three files were updated** +```bash +grep -l "map_provider_key\|vector_search\|test_actions\|scramble_credentials" \ + /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/*/SKILL.md +``` +Expected: 3 files listed. + +--- + +### Task 4: Scaffold new stackone-connector-builder plugin + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json` +- Create all skill directories + +**Step 1: Create directories** + +```bash +BASE="/Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder" +mkdir -p "$BASE/.claude-plugin" +mkdir -p "$BASE/skills/stackone-connector-builder" +mkdir -p "$BASE/skills/setup-connector" +mkdir -p "$BASE/skills/configure-auth" +mkdir -p "$BASE/skills/discover-actions" +mkdir -p "$BASE/skills/build-config" +mkdir -p "$BASE/skills/validate-connector" +mkdir -p "$BASE/skills/test-connector" +mkdir -p "$BASE/references" +``` + +**Step 2: Write plugin.json** + +```json +{ + "name": "stackone-connector-builder", + "version": "1.0.0", + "description": "Interactive wizard for building generic Falcon connectors. Guides builders through provider setup, authentication, action discovery (scoped or maximal), config generation, validation, and testing with full cleanup.", + "author": { + "name": "StackOne", + "email": "engineering@stackone.com" + }, + "license": "MIT", + "keywords": ["connector", "falcon", "generic", "stackone", "builder", "discovery"] +} +``` + +**Step 3: Copy shared references from unified builder** + +```bash +BASE="/Users/cameroncarlin/Projects/connectors-template/.claude/plugins" +cp "$BASE/stackone-unified-builder/references/connector-patterns.md" \ + "$BASE/stackone-connector-builder/references/" +cp "$BASE/stackone-unified-builder/references/cli-commands.md" \ + "$BASE/stackone-connector-builder/references/" +``` + +**Step 4: Verify** +```bash +ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder/ +``` +Expected: `.claude-plugin/`, `skills/`, `references/` present. + +--- + +### Task 5: Write auth-patterns.md reference + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/references/auth-patterns.md` + +**Step 1: Write the file** + +```markdown +# Authentication Patterns Reference + +Detailed YAML patterns for every authentication type supported by the Falcon framework. +Used by the `configure-auth` sub-skill. + +## Supported Auth Types + +| Type | When to use | +|------|-------------| +| `custom` (API Key / Bearer) | Static token or key passed in header or query param | +| `custom` (Basic Auth) | Username + password base64-encoded in Authorization header | +| `oauth2` | User-facing OAuth 2.0 authorization code flow | +| `custom` (Client Credentials) | Machine-to-machine OAuth 2.0 with client_id + client_secret | +| `custom` (Custom Headers) | Any combination of static headers | + +> **Note:** Falcon only supports `type: custom` and `type: oauth2`. All non-OAuth flows use `type: custom` with appropriate args and headers. + +--- + +## API Key / Bearer Token + +```yaml +authentication: + type: custom + args: + api_key: + value: "${PROVIDER_API_KEY}" + headers: + Authorization: "Bearer ${args.api_key.value}" +baseUrl: "https://api.provider.com/v1" +``` + +**`.env` variable:** `PROVIDER_API_KEY=your_key_here` + +--- + +## API Key in Query Parameter + +```yaml +authentication: + type: custom + args: + api_key: + value: "${PROVIDER_API_KEY}" +baseUrl: "https://api.provider.com/v1" +# Pass as query param in each action's entrypointUrl: +# entrypointUrl: "/employees?api_key=${args.api_key.value}" +``` + +--- + +## Basic Auth (Username + Password) + +```yaml +authentication: + type: custom + args: + username: + value: "${PROVIDER_USERNAME}" + password: + value: "${PROVIDER_PASSWORD}" + headers: + Authorization: "Basic ${base64(args.username.value + ':' + args.password.value)}" +baseUrl: "https://api.provider.com/v1" +``` + +**`.env` variables:** `PROVIDER_USERNAME=user`, `PROVIDER_PASSWORD=pass` + +--- + +## OAuth 2.0 (Authorization Code Flow) + +```yaml +authentication: + type: oauth2 + clientId: "${PROVIDER_CLIENT_ID}" + clientSecret: "${PROVIDER_CLIENT_SECRET}" + tokenUrl: "https://auth.provider.com/oauth/token" + authorizationUrl: "https://auth.provider.com/oauth/authorize" + scopes: + - read:employees + - read:departments +baseUrl: "https://api.provider.com/v1" +``` + +**`.env` variables:** `PROVIDER_CLIENT_ID=id`, `PROVIDER_CLIENT_SECRET=secret` + +--- + +## Client Credentials (Machine-to-Machine OAuth) + +```yaml +authentication: + type: custom + args: + access_token: + value: "${PROVIDER_ACCESS_TOKEN}" + headers: + Authorization: "Bearer ${args.access_token.value}" +baseUrl: "https://api.provider.com/v1" +``` + +Obtain `access_token` via a separate token exchange step outside the connector. + +**`.env` variable:** `PROVIDER_ACCESS_TOKEN=token` + +--- + +## Multiple Headers + +```yaml +authentication: + type: custom + args: + api_key: + value: "${PROVIDER_API_KEY}" + account_id: + value: "${PROVIDER_ACCOUNT_ID}" + headers: + X-API-Key: "${args.api_key.value}" + X-Account-Id: "${args.account_id.value}" + Content-Type: "application/json" +baseUrl: "https://api.provider.com/v1" +``` + +--- + +## Credential Variable Naming Convention + +Variables in `.env` follow: `_` + +| Provider | Credential | Variable name | +|----------|-----------|--------------| +| BambooHR | API Key | `BAMBOOHR_API_KEY` | +| Workday | Client ID | `WORKDAY_CLIENT_ID` | +| Salesforce | Access Token | `SALESFORCE_ACCESS_TOKEN` | +| HiBob | Username | `HIBOB_USERNAME` | + +--- + +## Common Auth Errors + +| Error | Cause | Fix | +|-------|-------|-----| +| `401 Unauthorized` | Wrong credentials or expired token | Check `.env` values, re-authenticate | +| `403 Forbidden` | Valid credentials, insufficient permissions | Add required scopes or API permissions | +| `${args.api_key.value}` appears literally in request | Interpolation not working | Ensure `args` block nests under `authentication` | +| `base64 is not defined` | Basic auth YAML error | Use the literal string format shown above | +``` + +**Step 2: Verify** +```bash +wc -l /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder/references/auth-patterns.md +``` +Expected: 100+ lines. + +--- + +### Task 6: Write orchestrator SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: stackone-connector-builder +description: Interactive wizard for building a generic Falcon connector. Guides builders through provider setup, authentication, action discovery (scoped or maximal), YAML config generation, validation, and live testing with cleanup. Auto-triggers when someone asks to build a new connector, add a new provider, or create a Falcon config. +invoke: build-connector +--- + +# Build Connector + +End-to-end wizard for building a generic StackOne Falcon connector. +Use this when you want to expose a provider's raw API responses — no schema mapping required. +For connectors that normalise data to a standard schema, use `/build-unified-connector` instead. + +## Quick Reference + +| Step | Command | What it does | +|------|---------|-------------| +| 1 | `/setup-connector` | Provider name, index check, CLI pull or scaffold | +| 2 | `/configure-auth` | Set up authentication (API key, OAuth2, Basic Auth) | +| 3 | `/discover-actions` | Choose scoped actions or discover everything | +| 4 | `/build-config` | Generate YAML for all confirmed actions | +| 5 | `/validate-connector` | Validate the YAML config | +| 6 | `/test-connector` | Live test + clean up all test records | + +Progress is saved to `.connector-build-session.json` — pause and resume any time. + +--- + +## Starting the Wizard + +Check for an existing `.connector-build-session.json`. If found: +> "Found an existing session: +> - **Provider:** `{{provider}}` +> - **Last step:** `{{session_step}}` +> +> Resume or restart?" + +If no session, greet: +> "Welcome to the StackOne connector builder. I'll help you build a Falcon connector that exposes `{{provider}}`'s API through StackOne. +> +> If you want to map the data to a standard schema (HRIS, ATS, CRM, etc.), use `/build-unified-connector` instead." + +--- + +## Steps + +### Step 1 — Setup +Execute `/setup-connector` logic. Saves: `provider`, `provider_key`, `cli_available`, `connector_exists`, `connector_path`. + +### Step 2 — Configure Auth +Execute `/configure-auth` logic. Saves: `auth_type`. Writes auth block to connector YAML. + +### Step 3 — Discover Actions +Execute `/discover-actions` logic. Saves: `discovery_mode`, `action_scope`, `use_case`. + +### Step 4 — Build Config +Execute `/build-config` logic. Writes action YAML to `src/configs/{{provider}}/`. + +### Step 5 — Validate +Execute `/validate-connector` logic. Saves: `validated: true`. + +### Step 6 — Test +Execute `/test-connector` logic. Saves: `tested: true`, `test_artifacts`, `completed_at`. + +--- + +## Session File Schema + +```json +{ + "provider": "workday", + "provider_key": "workday", + "connector_path": "src/configs/workday", + "cli_available": true, + "connector_exists": false, + "auth_type": "oauth2", + "discovery_mode": "scoped", + "action_scope": { + "employees": ["list", "get", "create"], + "departments": ["list", "get"] + }, + "use_case": "Read employee and department data", + "known_limitations": [], + "test_artifacts": [ + { "resource": "employees", "id": "EMP_test_001", "cleaned_up": true }, + { "resource": "departments", "id": "DEP_test_007", "cleaned_up": false, "reason": "no delete endpoint" } + ], + "validated": false, + "tested": false, + "session_step": "build-config", + "completed_at": null +} +``` + +--- + +## Rules + +- Do not write YAML until Step 4 (`build-config`) +- Do not run tests until Step 5 (`validate-connector`) passes +- Always clean up test records — log anything that cannot be removed +- Always run `scramble_credentials` after testing +- Never ask for information already in the session +``` + +--- + +### Task 7: Write setup-connector SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: setup-connector +description: Step 1 of building a generic Falcon connector. Checks StackOne's provider index, detects CLI availability, and either pulls an existing connector base config or scaffolds a new one. +invoke: setup-connector +--- + +# Setup Connector + +Step 1 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. If `provider` is already set: +> "Resuming — provider already set to `{{provider}}`. Run `/configure-auth` to continue." + +--- + +## Step 1: Ask for provider name + +Ask: +> "What provider are you building a connector for? (e.g., `workday`, `bamboohr`, `rippling`)" + +Save as `provider` (lowercase, hyphenated). + +--- + +## Step 2: Check StackOne provider index + +``` +map_provider_key("{{provider}}") +``` + +If a key is returned, save as `provider_key`. + +``` +get_provider_actions("{{provider_key}}") +``` + +If indexed actions exist: +> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference." + +If not found: +> "`{{provider}}` isn't indexed yet — we'll discover actions from scratch in Step 3." + +--- + +## Step 3: Detect CLI availability + +```bash +npx @stackone/cli --version +``` + +Save `cli_available: true/false`. + +If unavailable: +> "StackOne CLI isn't available. I'll guide manual scaffolding instead." + +--- + +## Step 4: Pull or scaffold + +**If CLI available and provider exists in index:** +```bash +npx @stackone/cli pull {{provider}} +``` +Verify `src/configs/{{provider}}/` was created. Save `connector_exists: true`. + +**If scaffolding from scratch:** +```bash +npx @stackone/cli scaffold {{provider}} +``` +Or if CLI unavailable, create `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` manually: +```yaml +name: {{provider}} +version: "1.0" +authentication: {} # configured in next step +baseUrl: "" # set in next step +actions: [] +``` +Save `connector_exists: false`. + +Save `connector_path: "src/configs/{{provider}}"`. + +--- + +## Handoff + +> "Setup complete. ✓ Connector at `src/configs/{{provider}}/` +> +> Next: configure authentication. +> Run `/configure-auth` to continue." + +Update `session_step` to `"configure-auth"`. +``` + +--- + +### Task 8: Write configure-auth SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: configure-auth +description: Step 2 of building a generic Falcon connector. Asks the builder which authentication type the provider uses, generates the correct YAML auth block, and writes it into the connector config file. +invoke: configure-auth +--- + +# Configure Auth + +Step 2 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Configuring auth for `{{provider}}`." + +Read `${CLAUDE_PLUGIN_ROOT}/references/auth-patterns.md` before starting. + +--- + +## Step 1: Identify auth type + +Ask: +> "How does `{{provider}}`'s API authenticate requests? +> +> **A) API Key / Bearer token** — static key passed in a header +> **B) OAuth 2.0** — users authorise via a browser flow +> **C) Basic Auth** — username and password +> **D) API Key in query parameter** — key appended to the URL +> **E) Something else** — describe it and I'll help figure it out +> +> (A/B/C/D/E)" + +--- + +## Step 2: Collect credentials + +Ask for the specific values needed: + +- **A / D (API Key):** "What's the header or query param name? (e.g., `Authorization`, `X-API-Key`, `api_key`)" +- **B (OAuth2):** "Do you have the token URL and authorization URL? What scopes are needed?" +- **C (Basic Auth):** "Is this username + password, or username + API key as password?" +- **E (Other):** Gather details and match to the closest pattern in `auth-patterns.md` + +--- + +## Step 3: Write auth block into connector YAML + +Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. + +Replace the `authentication: {}` placeholder (or existing auth block) with the correct pattern from `auth-patterns.md`. + +Also set `baseUrl` if the builder knows it: +> "What is the base URL for `{{provider}}`'s API? (e.g., `https://api.provider.com/v1`)" + +If unknown, leave a placeholder: `baseUrl: "# TODO: set provider base URL"`. + +--- + +## Step 4: Show required .env variables + +Tell the builder exactly which variables to add to `.env`: +> "Add these to your `.env` file before testing: +> ``` +> {{PROVIDER_CREDENTIAL_VARS}} +> ```" + +--- + +## Step 5: Confirm + +Show the written auth block and ask: +> "Does this look right? The credentials stay in `.env` — the YAML only references them by variable name." + +Apply any corrections. + +Save `auth_type` to session. + +--- + +## Handoff + +> "Auth configured. ✓ +> +> Next: choose which actions to build. +> Run `/discover-actions` to continue." + +Update `session_step` to `"discover-actions"`. +``` + +--- + +### Task 9: Write discover-actions SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: discover-actions +description: Step 3 of building a generic Falcon connector. Lets the builder choose between scoped discovery (use-case driven) or maximal discovery (finds every available API endpoint using the StackOne discover_actions MCP tool). Saves the confirmed action scope to session. +invoke: discover-actions +--- + +# Discover Actions + +Step 3 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Discovering actions for `{{provider}}`." + +--- + +## Step 1: Choose discovery mode + +Ask: +> "How much of `{{provider}}`'s API do you want to cover? +> +> **A) Scoped** — Tell me your use case and I'll recommend the right actions +> **B) Maximal** — Discover every available endpoint (takes 5–15 minutes, uses autonomous research) +> +> (A/B)" + +Save `discovery_mode: "scoped"` or `"maximal"`. + +--- + +## Path A: Scoped discovery + +### A1: Ask about use case + +> "Describe what you need this connector to do. Examples: +> - "Read employee and department data into our platform" +> - "Let users create and update time-off requests" +> - "Sync job postings to our job board" +> +> What's your use case?" + +Save `use_case` to session. + +### A2: Look up relevant actions + +Run a vector search to find related actions: +``` +vector_search("{{use_case}} {{provider}}") +``` + +Also check StackOne's known actions for this provider: +``` +get_provider_actions("{{provider_key}}") +``` + +### A3: Present recommended action set + +Based on the use case and search results, present a recommended scope: + +> "Based on your use case, here are the actions I recommend: +> +> | Resource | Actions | Endpoints | +> |----------|---------|----------| +> | `employees` | list, get | `GET /employees`, `GET /employees/{id}` | +> | `departments` | list | `GET /departments` | +> +> Does this cover what you need? You can add or remove anything." + +Allow the builder to adjust freely. + +### A4: Flag known limitations + +If `get_provider_actions` returned coverage data, note any gaps: +> "Note: `{{provider}}` doesn't appear to support `create` on employees via API — only `list` and `get` are confirmed." + +### A5: Save to session + +```json +{ + "discovery_mode": "scoped", + "action_scope": { + "employees": ["list", "get"], + "departments": ["list"] + }, + "use_case": "Read employee and department data" +} +``` + +--- + +## Path B: Maximal discovery + +### B1: Check S3 cache first + +``` +map_provider_key("{{provider}}") +get_provider_actions("{{provider_key}}") +``` + +If cached data exists with many actions: +> "Found {{N}} actions in the StackOne index for `{{provider}}`. I can use this data directly — no need to wait for a full discovery run." + +Use cached data and skip to B4. + +### B2: Launch autonomous discovery + +``` +discover_actions({ provider: "{{provider}}", maxIterations: 30 }) +``` + +Returns a `taskId` immediately. Tell the builder: +> "Launched autonomous discovery for `{{provider}}`. This typically takes 5–15 minutes. I'll poll for results — you can continue working in another tab." + +### B3: Poll for completion + +Every 60–90 seconds: +``` +get_discover_actions_task_status("{{taskId}}", "{{provider}}") +``` + +Status: `pending` → `running` → `complete` + +When `running`, report progress: +> "Still discovering... found {{N}} actions so far." + +### B4: Run version analysis + +Once discovery completes, extract endpoints and run: +``` +analyze_versioning({ provider: "{{provider}}", endpoints: [...], maxIterations: 5 }) +``` + +Poll with `get_analyze_versioning_task_status`. This identifies deprecated endpoints and version conflicts. + +### B5: Present full action list + +> "Discovery complete. Found {{N}} actions across {{M}} resources: +> +> | Resource | Actions found | +> |----------|-------------| +> | `employees` | list, get, create, update | +> | `departments` | list, get | +> | `time_off` | list, get, create, delete | +> | ... | +> +> Which would you like to include? (all / select specific ones)" + +### B6: Save to session + +```json +{ + "discovery_mode": "maximal", + "action_scope": { ... all confirmed actions ... }, + "use_case": "Full API coverage" +} +``` + +--- + +## Handoff + +> "Action scope confirmed. ✓ +> +> {{N}} actions across {{M}} resources. +> +> Next: generate the connector YAML. +> Run `/build-config` to continue." + +Update `session_step` to `"build-config"`. +``` + +--- + +### Task 10: Write build-config SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: build-config +description: Step 4 of building a generic Falcon connector. Generates YAML action configurations for all confirmed actions using actionType custom, writes partial files for each resource, and updates the main connector YAML with $ref links. +invoke: build-config +--- + +# Build Config + +Step 4 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Building config for `{{provider}}` — {{N}} actions across {{M}} resources." + +Read `${CLAUDE_PLUGIN_ROOT}/references/connector-patterns.md` before writing any YAML. + +If `action_scope` is missing: +> "No action scope found. Run `/discover-actions` first." + +--- + +## Step 1: Ask for endpoint details (if not already known) + +If endpoint URLs weren't captured during discovery, ask for each resource: +> "What is the API endpoint for `{{resource}}`? (e.g., `/employees`, `/v2/employees`)" + +Use provider API docs if the builder can share a link, or reference any data from `get_provider_actions`. + +--- + +## Step 2: For each resource, write a partial YAML file + +Create `src/configs/{{provider}}/{{provider}}.{{resource}}.s1.partial.yaml`. + +Use the non-unified action pattern from `connector-patterns.md`. Each action should be: + +```yaml +- name: list_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}" + entrypointHttpMethod: GET + inputs: + page: + type: number + required: false + per_page: + type: number + required: false + steps: + - type: request + id: fetch_{{resource}} +``` + +For `get` actions: +```yaml +- name: get_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: GET + inputs: + id: + type: string + required: true + steps: + - type: request + id: fetch_{{resource}} +``` + +For `create` actions: +```yaml +- name: create_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}" + entrypointHttpMethod: POST + inputs: + body: + type: object + required: true + steps: + - type: request + id: create_{{resource}} +``` + +For `update` actions: +```yaml +- name: update_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: PATCH + inputs: + id: + type: string + required: true + body: + type: object + required: true + steps: + - type: request + id: update_{{resource}} +``` + +For `delete` actions: +```yaml +- name: delete_{{resource}} + actionType: custom + entrypointUrl: "/{{resource}}/${inputs.id}" + entrypointHttpMethod: DELETE + inputs: + id: + type: string + required: true + steps: + - type: request + id: delete_{{resource}} +``` + +--- + +## Step 3: Update main connector YAML with $refs + +Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. + +Add a `$ref` for each partial under `actions`: +```yaml +actions: + - $ref: "./{{provider}}.employees.s1.partial.yaml" + - $ref: "./{{provider}}.departments.s1.partial.yaml" +``` + +--- + +## Step 4: Review with builder + +Show the file list created: +> "Config written: +> - `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` (updated) +> - `src/configs/{{provider}}/{{provider}}.employees.s1.partial.yaml` +> - ..." + +Ask: +> "Do the endpoint URLs and HTTP methods look right? Any actions need adjusting before we validate?" + +Apply corrections. + +--- + +## Handoff + +> "Config built. ✓ +> +> Next: validate the YAML. +> Run `/validate-connector` to continue." + +Update `session_step` to `"validate-connector"`. +``` + +--- + +### Task 11: Write generic validate-connector SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md` + +**Step 1: Write the file** + +This is simpler than the unified version — no `schemaType`, `fieldConfigs`, or `map_fields` steps to check. + +```markdown +--- +name: validate-connector +description: Step 5 of building a generic Falcon connector. Runs stackone validate, interprets errors, guides fixes, and confirms the config is structurally correct before live testing. +invoke: validate-connector +--- + +# Validate Connector + +Step 5 of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. Confirm: +> "Validating `src/configs/{{provider}}/`" + +Read `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the error table. + +--- + +## Step 1: Run validation + +**If `cli_available` is true:** +```bash +npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.yaml +``` + +**If CLI unavailable (manual review):** + +Read each `.yaml` and `.partial.yaml` file and check: +- Every action has `entrypointUrl` and `entrypointHttpMethod` +- Valid `actionType` values: `custom`, `list`, `get`, `create`, `update`, `delete` +- Actions that take an ID have `${inputs.id}` in the URL and `id` in `inputs` +- `create` and `update` actions have a `body` input +- All `$ref` paths in the main connector point to files that exist +- No tabs — 2-space indentation throughout +- `baseUrl` is set (not a placeholder) +- `authentication` block is populated (not `{}`) + +--- + +## Step 2: Fix errors + +For each error, explain and fix: + +**Missing `entrypointUrl`:** +```yaml +entrypointUrl: "/{{resource}}" +entrypointHttpMethod: GET +``` + +**Wrong `actionType`:** +> Valid: `custom`, `list`, `get`, `create`, `update`, `delete` + +**Missing `inputs.id` for get/update/delete:** +```yaml +inputs: + id: + type: string + required: true +``` + +**Bad `$ref` path:** +> Check filename: `{{provider}}.{{resource}}.s1.partial.yaml` + +**Empty auth block:** +> Run `/configure-auth` to set up authentication. + +Re-run after each fix. Repeat until clean. + +--- + +## Step 3: Confirm + +> "Validation passed. ✓ +> +> - Provider: `{{provider}}` +> - Resources: {{resources}} +> - Actions: {{action_scope}}" + +Save `validated: true` to session. + +--- + +## Handoff + +> "Ready for testing. Ensure your `.env` credentials are set. +> +> Run `/test-connector` to continue." + +Update `session_step` to `"test-connector"`. +``` + +--- + +### Task 12: Write generic test-connector SKILL.md + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md` + +**Step 1: Write the file** + +```markdown +--- +name: test-connector +description: Step 6 (final) of building a generic Falcon connector. Tests every action live against the provider API, verifies responses, cleans up all test records created during testing, and produces a cleanup report. +invoke: test-connector +--- + +# Test Connector + +Step 6 (final step) of the generic connector build process. + +## Session File + +Read `.connector-build-session.json`. + +If `validated` is not `true`: +> "Connector hasn't been validated yet. Run `/validate-connector` first." + +Initialise `test_artifacts: []` in session if not present. + +--- + +## Step 1: Confirm credentials + +Ask: +> "Are your `.env` credentials ready for `{{provider}}`? +> You'll need: `{{PROVIDER_CREDENTIAL_VARS}}`" + +Wait for confirmation. + +--- + +## Step 2: Test each action + +Test in this order for each resource: `list` → `get` → `create` → `update` → `delete`. + +**If MCP tools are accessible:** +``` +test_actions({ + provider: "{{provider}}", + actions: ["list_{{resource}}", "get_{{resource}}"] +}) +``` +Poll with `get_test_actions_task_status(taskId)`. + +**If running manually:** +```bash +npx @stackone/cli test {{provider}} list_{{resource}} +npx @stackone/cli test {{provider}} get_{{resource}} --input '{"id": "test-id"}' +``` + +--- + +## Step 3: Track test artifacts + +Every time a `create` action succeeds, immediately record the new record: + +```json +{ "resource": "{{resource}}", "id": "{{returned_id}}", "cleaned_up": false } +``` + +Add to `test_artifacts` in session. + +--- + +## Step 4: Verify responses + +**`list` actions:** +- Response is an array or has a `data` array +- At least one record returned (or empty array with no error) + +**`get` actions:** +- Single object returned +- Object has an `id` field + +**`create` actions:** +- Record created (2xx response) +- Response contains the new record's `id` + +**`update` actions:** +- Changes reflected in response or confirmed with follow-up `get` + +**`delete` actions:** +- 200, 202, or 204 response +- Follow-up `get` returns 404 or empty + +--- + +## Step 5: Clean up after every create + +After verifying each `create` + `update`: + +**If `delete` is in scope for this resource:** +```bash +npx @stackone/cli test {{provider}} delete_{{resource}} --input '{"id": "{{test_record_id}}"}' +``` + +Verify with a follow-up `get` (expect 404). + +Mark in session: `"cleaned_up": true`. + +**If `delete` is NOT in scope:** + +Attempt soft cleanup in this order: +1. Look for an `update` action — set status to `inactive`, `archived`, or `cancelled` +2. Look for a `cancel` or `deactivate` action +3. If none available: log the record as irremovable + +Mark in session: `"cleaned_up": false`, `"reason": "no delete endpoint"`. + +--- + +## Step 6: Diagnose failures + +**401/403 — Auth error:** +> "Authentication failed. Check your `.env` credentials and the `authentication` block in the connector YAML." + +**404 — Wrong URL:** +> "Endpoint not found. Verify `entrypointUrl` matches the provider's API docs." + +**Empty response / wrong shape:** +> "The response structure doesn't match expectations. Use `--debug` to inspect the raw response and update `entrypointUrl` or HTTP method if needed." + +**Create succeeds but update/delete fails:** +> "The record was created but subsequent operations failed — likely the ID format differs between create response and path parameter. Check `${inputs.id}` matches the ID field in the create response." + +Fix the issue in the partial YAML and re-run the affected action. Re-clean up any leftover records. + +--- + +## Step 7: Scramble credentials + +After all tests pass (or are resolved): + +``` +scramble_credentials("{{provider}}") +``` + +This removes real credentials from the config history. + +--- + +## Step 8: Cleanup report + final summary + +> "All tests complete. +> +> **Test cleanup summary:** +> | Resource | Record ID | Status | +> |----------|-----------|--------| +> | `employees` | EMP_test_001 | ✓ deleted | +> | `departments` | DEP_test_007 | ⚠ not deleted — no delete endpoint | +> +> **Actions verified:** +> | Resource | list | get | create | update | delete | +> |----------|------|-----|--------|--------|--------| +> | `employees` | ✓ | ✓ | ✓ | ✓ | ✓ | +> | `departments` | ✓ | ✓ | — | — | — | +> +> **Next steps:** +> 1. Commit: `git add src/configs/{{provider}}/ && git commit -m 'feat: add {{provider}} connector'` +> 2. Address any undeleted test records manually (IDs listed above) +> 3. Open a PR to the connectors repository" + +Save `tested: true`, `completed_at: "{{datetime}}"` to session. +``` + +--- + +### Task 13: Write README.md for generic plugin + +**Files:** +- Create: `.claude/plugins/stackone-connector-builder/README.md` + +**Step 1: Write the file** + +```markdown +# stackone-connector-builder + +Interactive wizard for building generic Falcon connectors. Guides builders through provider setup, authentication, action discovery, YAML config generation, validation, and live testing — with full test record cleanup. + +For connectors that normalise data to a standard schema (HRIS, ATS, CRM, etc.), use `stackone-unified-builder` instead. + +## Usage + +### Full wizard + +```bash +/build-connector +``` + +### Individual steps + +| Command | Step | +|---------|------| +| `/setup-connector` | 1. Provider setup and scaffold | +| `/configure-auth` | 2. Authentication configuration | +| `/discover-actions` | 3. Scoped or maximal action discovery | +| `/build-config` | 4. Generate action YAML | +| `/validate-connector` | 5. Validate config structure | +| `/test-connector` | 6. Live test + cleanup | + +## Discovery modes + +**Scoped** — Describe your use case, get a focused action set. Fast. + +**Maximal** — Autonomous discovery of every available API endpoint using StackOne's `discover_actions` tool. Takes 5–15 minutes but finds everything. + +## Test cleanup + +`/test-connector` records every ID created during testing and deletes them after verification. A cleanup report is shown at the end listing anything that couldn't be removed. + +## Requirements + +- `connectors-template` project structure +- Node.js (for StackOne CLI — optional but recommended) +- StackOne MCP tools (for `discover_actions`, `test_actions`, etc.) +- Provider API credentials in `.env` +``` + +--- + +### Task 14: Final verification + +**Step 1: Confirm unified builder rename is complete** +```bash +ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/ +``` +Expected: `stackone-unified-builder/` folder present (not `stackone-connector-builder/`). + +**Step 2: Confirm new generic plugin structure** +```bash +find /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder -type f | sort +``` + +Expected output: +``` +.claude-plugin/plugin.json +README.md +references/auth-patterns.md +references/cli-commands.md +references/connector-patterns.md +skills/stackone-connector-builder/SKILL.md +skills/setup-connector/SKILL.md +skills/configure-auth/SKILL.md +skills/discover-actions/SKILL.md +skills/build-config/SKILL.md +skills/validate-connector/SKILL.md +skills/test-connector/SKILL.md +``` + +**Step 3: Confirm no invoke command conflicts** +```bash +grep -r "^invoke:" /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/*/skills/*/SKILL.md | sort +``` +Expected: `/build-connector` appears once, `/build-unified-connector` appears once. All others unique. + +**Step 4: Done** — notify user. diff --git a/docs/plans/2026-03-20-connector-builder-plugins-redesign.md b/docs/plans/2026-03-20-connector-builder-plugins-redesign.md new file mode 100644 index 0000000..c5ddf79 --- /dev/null +++ b/docs/plans/2026-03-20-connector-builder-plugins-redesign.md @@ -0,0 +1,128 @@ +# Connector Builder Plugins Redesign + +## Goal + +Rename the existing unified connector builder plugin to `stackone-unified-builder` (invoke: `/build-unified-connector`), and create a new sibling plugin `stackone-connector-builder` (invoke: `/build-connector`) for building generic (non-unified) Falcon connectors. + +## Architecture + +Both plugins live under `connectors-template/.claude/plugins/`, share the same session file pattern (`.connector-build-session.json`), and explicitly reference StackOne MCP tools. The unified builder maps provider data to a schema via `fieldConfigs`; the generic builder outputs raw provider responses via `actionType: custom`. + +--- + +## Part 1: Rename stackone-connector-builder → stackone-unified-builder + +### Changes required + +- Rename directory: `.claude/plugins/stackone-connector-builder/` → `.claude/plugins/stackone-unified-builder/` +- Update `plugin.json`: name, description, keywords +- Update main orchestrator `SKILL.md`: name, description, invoke command (`/build-unified-connector`) +- Add explicit MCP tool references to `check-connector`, `map-fields`, and `test-connector` skills where missing + +### MCP tools to add to unified builder + +| Skill | Tools to add | +|-------|-------------| +| `check-connector` | `map_provider_key`, `get_provider_actions` — check S3 index before CLI | +| `map-fields` | `get_stackone_actions`, `vector_search` — look up schema action coverage | +| `test-connector` | `test_actions`, `scramble_credentials` | + +--- + +## Part 2: New stackone-connector-builder (generic) + +### Plugin structure + +``` +.claude/plugins/stackone-connector-builder/ +├── .claude-plugin/ +│ └── plugin.json +├── README.md +├── skills/ +│ ├── stackone-connector-builder/ ← /build-connector (orchestrator) +│ ├── setup-connector/ ← /setup-connector (Step 1) +│ ├── configure-auth/ ← /configure-auth (Step 2) +│ ├── discover-actions/ ← /discover-actions (Step 3) +│ ├── build-config/ ← /build-config (Step 4) +│ ├── validate-connector/ ← /validate-connector (Step 5) +│ └── test-connector/ ← /test-connector (Step 6) +└── references/ + ├── connector-patterns.md ← symlink/copy from unified + ├── cli-commands.md ← symlink/copy from unified + └── auth-patterns.md ← new: detailed auth reference +``` + +### Steps + +| Step | Skill | Key behaviour | +|------|-------|--------------| +| 1 | `setup-connector` | Provider name → `map_provider_key` → `get_provider_actions` (S3 check) → CLI pull or scaffold | +| 2 | `configure-auth` | API key / OAuth2 / Basic Auth / custom; write auth block to connector YAML | +| 3 | `discover-actions` | Scoped (ask use case, recommend actions) OR maximal (`discover_actions` MCP, async 5–15 min) | +| 4 | `build-config` | Write YAML with all confirmed actions (`actionType: custom`, raw response, no fieldConfigs) | +| 5 | `validate-connector` | `stackone validate`; interpret and fix errors | +| 6 | `test-connector` | Test each action live; **clean up after itself** | + +### discover-actions detail + +**Scoped path:** +1. Ask use case in plain language +2. Run `get_stackone_actions` + `vector_search` to find relevant actions +3. Present recommended action set, let builder adjust +4. Save `action_scope` to session + +**Maximal path:** +1. Run `map_provider_key` to get exact provider key +2. Check `get_provider_actions` for cached data +3. If no cache: launch `discover_actions(provider, maxIterations: 30)` → get `taskId` +4. Poll `get_discover_actions_task_status` every 60–90s +5. Run `analyze_versioning` on discovered endpoints +6. Present full discovered action list for builder confirmation +7. Save `action_scope` to session + +### test-connector cleanup behaviour + +Test order: `list` → `get` → `create` → `update` → `delete` + +**Cleanup rules:** +- Record every `create` response and store the new record ID +- After `update` is verified, attempt `delete` on the test record +- After `delete`, confirm with a follow-up `get` (expect 404) +- If `delete` is not in scope: attempt soft cleanup (archive/deactivate/cancel if available) +- If no cleanup possible: log the record ID and warn the builder +- Use `scramble_credentials` after all tests pass + +**Cleanup report** (shown at end): +``` +Test cleanup summary: +✓ employees/EMP_test_001 — created and deleted +✓ time_off/TOF_test_042 — created and deleted +⚠ department/DEP_test_007 — created, no delete endpoint; record remains +``` + +### Session file schema (generic) + +```json +{ + "provider": "workday", + "connector_path": "src/configs/workday", + "cli_available": true, + "connector_exists": false, + "auth_type": "oauth2", + "discovery_mode": "scoped", + "action_scope": { + "employees": ["list", "get", "create"], + "departments": ["list", "get"] + }, + "use_case": "Read employee and department data", + "known_limitations": [], + "test_artifacts": [ + { "resource": "employees", "id": "EMP_test_001", "cleaned_up": true }, + { "resource": "departments", "id": "DEP_test_007", "cleaned_up": false, "reason": "no delete endpoint" } + ], + "validated": false, + "tested": false, + "session_step": "build-config", + "completed_at": null +} +``` diff --git a/examples/ashby/ashby.candidates.s1.partial.yaml b/examples/ashby/ashby.candidates.s1.partial.yaml index 66186dc..658f6f2 100644 --- a/examples/ashby/ashby.candidates.s1.partial.yaml +++ b/examples/ashby/ashby.candidates.s1.partial.yaml @@ -52,6 +52,9 @@ actions: collection: true result: data: $.steps.fetch_candidates.output.data + nextCursor: $.steps.fetch_candidates.output.data.nextCursor + syncToken: $.steps.fetch_candidates.output.data.syncToken + moreDataAvailable: $.steps.fetch_candidates.output.data.moreDataAvailable - actionId: get_candidate label: Get Candidate @@ -541,6 +544,9 @@ actions: collection: true result: data: $.steps.fetch_notes.output.data + nextCursor: $.steps.fetch_notes.output.data.nextCursor + syncToken: $.steps.fetch_notes.output.data.syncToken + moreDataAvailable: $.steps.fetch_notes.output.data.moreDataAvailable - actionId: create_candidate_note label: Create Candidate Note diff --git a/examples/bamboo-hr/bamboo-hr.connector.s1.yaml b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml index f37b661..7134b62 100644 --- a/examples/bamboo-hr/bamboo-hr.connector.s1.yaml +++ b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml @@ -1,4 +1,5 @@ StackOne: 1.0.0 +schemaType: unified info: title: BambooHR Unified key: bamboo-hr-unified diff --git a/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml index 14c8816..0426fc1 100644 --- a/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml +++ b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml @@ -9,10 +9,6 @@ entrypointUrl: /employees/directory entrypointHttpMethod: get - cursor: - enabled: true - pageSize: 50 - steps: - stepId: get_employees description: Fetch all employees from BambooHR employee directory @@ -52,7 +48,7 @@ expression: $.employeeNumber type: string - targetFieldKey: title - expression: $.prefix + expression: $.jobTitle type: string dataSource: $.steps.get_employees.output.data.employees @@ -90,7 +86,7 @@ description: Returns a single employee record by ID with full unified HRIS schema field mapping, including bank details and employment history. details: Makes three parallel-style API calls — main employee fields, directDeposit table for all bank accounts, and jobInfo table for full employment history — then combines them into a single unified response. Bank details and employment history are fetched as complete arrays in one call each. 404s on table endpoints are handled gracefully and return empty arrays. resources: https://documentation.bamboohr.com/reference/get-employee - entrypointUrl: /employees/${inputs.id} + entrypointUrl: /employees/:id entrypointHttpMethod: get inputs: @@ -113,7 +109,7 @@ value: application/json in: headers - name: fields - value: id,firstName,lastName,dateOfBirth,homeEmail,terminationDate,mobilePhone,hireDate,address1,address2,city,zipCode,country,employmentHistoryStatus,employeeNumber,maritalStatus,prefix,ssn + value: id,firstName,lastName,dateOfBirth,homeEmail,terminationDate,mobilePhone,hireDate,address1,address2,city,zipCode,country,employmentHistoryStatus,employeeNumber,maritalStatus,jobTitle,ssn in: query - stepId: fetch_bank_details @@ -221,7 +217,7 @@ type: object expression: $.fetch_bank_details.data - targetFieldKey: title - expression: $.fetch_employee.prefix + expression: $.fetch_employee.jobTitle type: string - stepId: typecast_employee_data @@ -301,7 +297,7 @@ in: body required: false - name: title - description: The employee's name prefix (Mr, Ms, Dr, etc.) + description: The employee's job title type: string in: body required: false @@ -379,7 +375,7 @@ value: $.inputs.start_date in: body condition: '{{present(inputs.start_date)}}' - - name: prefix + - name: jobTitle value: $.inputs.title in: body condition: '{{present(inputs.title)}}' @@ -425,7 +421,7 @@ description: Updates an existing employee record in BambooHR. details: Updates one or more fields on an existing employee. Only provided fields will be updated — all body fields are optional except the employee ID path parameter. BambooHR uses POST (not PATCH) for employee field updates. resources: https://documentation.bamboohr.com/reference/update-employee - entrypointUrl: /employees/${inputs.id} + entrypointUrl: /employees/:id entrypointHttpMethod: post inputs: @@ -470,7 +466,7 @@ in: body required: false - name: title - description: The employee's name prefix (Mr, Ms, Dr, etc.) + description: The employee's job title type: string in: body required: false @@ -554,7 +550,7 @@ value: $.inputs.termination_date in: body condition: '{{present(inputs.termination_date)}}' - - name: prefix + - name: jobTitle value: $.inputs.title in: body condition: '{{present(inputs.title)}}' @@ -600,7 +596,7 @@ description: Terminates an employee in BambooHR by setting their termination date. details: BambooHR does not support hard deletion of employee records. This action terminates an employee by setting their termination date. The employee record remains accessible in the system after termination. resources: https://documentation.bamboohr.com/reference/update-employee - entrypointUrl: /employees/${inputs.id} + entrypointUrl: /employees/:id entrypointHttpMethod: post inputs: diff --git a/src/configs/breathehr/breathehr_v1-0-1.s1.yaml b/src/configs/breathehr/breathehr_v1-0-1.s1.yaml index 00377fe..4559d62 100644 --- a/src/configs/breathehr/breathehr_v1-0-1.s1.yaml +++ b/src/configs/breathehr/breathehr_v1-0-1.s1.yaml @@ -712,6 +712,7 @@ actions: data: $.steps.create_sickness.output.data # Blind Mapped as the provider gives 500 while creating with right payload (need to recheck) - actionId: create_employee + published: false categories: - hris actionType: create @@ -1331,7 +1332,7 @@ actions: response: collection: true result: - data: $.steps.get_all_other_leave_reasons_v1.output.data + data: $.steps.get_all_other_leave_reasons.output.data - actionId: approve_leave_request categories: - hris From 623fbc6358256b73e773d0487c3a24e601d6eb15 Mon Sep 17 00:00:00 2001 From: Cameron Carlin Date: Mon, 23 Mar 2026 11:23:58 +0000 Subject: [PATCH 08/13] Delete docs/plans/2026-03-20-connector-builder-plugins-implementation.md --- ...onnector-builder-plugins-implementation.md | 1577 ----------------- 1 file changed, 1577 deletions(-) delete mode 100644 docs/plans/2026-03-20-connector-builder-plugins-implementation.md diff --git a/docs/plans/2026-03-20-connector-builder-plugins-implementation.md b/docs/plans/2026-03-20-connector-builder-plugins-implementation.md deleted file mode 100644 index 84ee6ed..0000000 --- a/docs/plans/2026-03-20-connector-builder-plugins-implementation.md +++ /dev/null @@ -1,1577 +0,0 @@ -# Connector Builder Plugins Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Rename `stackone-connector-builder` → `stackone-unified-builder` (invoke: `/build-unified-connector`) and create a new `stackone-connector-builder` plugin for generic Falcon connector builds (invoke: `/build-connector`). - -**Architecture:** Both plugins live under `.claude/plugins/` in connectors-template. The rename is a directory move + content updates. The new generic plugin mirrors the unified structure but replaces schema/field-mapping steps with action discovery and raw config building, uses StackOne MCP tools throughout, and includes test cleanup behaviour. - -**Tech Stack:** Claude Code plugin system (SKILL.md format), StackOne MCP tools (`mcp__stackone-agent-global__*`), StackOne CLI (`npx @stackone/cli`), YAML connector configs, JSON session file for state. - ---- - -### Task 1: Rename plugin directory and update plugin.json - -**Files:** -- Rename: `.claude/plugins/stackone-connector-builder/` → `.claude/plugins/stackone-unified-builder/` -- Modify: `.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json` - -**Step 1: Rename the directory** - -```bash -mv /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder \ - /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder -``` - -**Step 2: Update plugin.json** - -Write this content to `.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json`: - -```json -{ - "name": "stackone-unified-builder", - "version": "1.0.0", - "description": "Interactive step-by-step wizard for building unified Falcon connectors. Walks external builders through schema selection, connector scaffolding, action scoping, field mapping, validation, and testing.", - "author": { - "name": "StackOne", - "email": "engineering@stackone.com" - }, - "license": "MIT", - "keywords": ["connector", "falcon", "unified", "schema", "stackone", "builder"] -} -``` - -**Step 3: Verify** -```bash -ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/ -cat /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/.claude-plugin/plugin.json -``` -Expected: directory exists, JSON has `"name": "stackone-unified-builder"`. - ---- - -### Task 2: Update unified builder orchestrator skill - -**Files:** -- Modify: `.claude/plugins/stackone-unified-builder/skills/stackone-connector-builder/SKILL.md` -- Rename folder: `skills/stackone-connector-builder/` → `skills/stackone-unified-builder/` - -**Step 1: Rename the skill folder** - -```bash -mv /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-connector-builder \ - /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder -``` - -**Step 2: Write updated SKILL.md** - -Write this content to `.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md`: - -```markdown ---- -name: stackone-unified-builder -description: Interactive wizard for building a unified Falcon connector from scratch. Guides external builders through schema selection, connector setup, action scoping, field mapping, validation, and testing. Auto-triggers when someone asks to build a new unified connector, map provider data to a schema, or integrate with a new provider using StackOne's unified API. -invoke: build-unified-connector ---- - -# Build Unified Connector - -End-to-end wizard for building a unified StackOne Falcon connector. - -## Quick Reference - -Run steps in order, or invoke any sub-skill directly to jump to that phase: - -| Step | Command | What it does | -|------|---------|-------------| -| 1 | `/choose-schema` | Pick your schema: built-in (A), import from file (B), or define inline (C) | -| — | `/import-schema` | Import schema fields from a CSV, JSON, YAML, or any schema document | -| 2 | `/check-connector` | Check if connector exists, pull or scaffold | -| 3 | `/scope-actions` | Decide which resources and operations to expose | -| 4 | `/map-fields` | Map provider API fields to your schema | -| 5 | `/validate-connector` | Validate the YAML configuration | -| 6 | `/test-connector` | Test live against the provider API | - -Each step saves progress to `.connector-build-session.json` — you can pause and resume at any time. - -`/import-schema` can be run standalone at any point to load schema fields from a document. It feeds directly into `/map-fields`. - ---- - -## Starting the Wizard - -Check for an existing `.connector-build-session.json`. If found: -> "Found an existing session: -> - **Provider:** `{{provider}}` -> - **Schema:** `{{schema}}` -> - **Last step completed:** `{{session_step}}` -> -> Would you like to: -> - **Resume** from `{{session_step}}` -> - **Restart** from the beginning (clears session)" - -If no session exists, greet the builder and proceed to Step 1: -> "Welcome to the StackOne unified connector builder. I'll guide you through building a connector that maps provider data to a standardised schema step by step." - ---- - -## Step 1 — Choose Schema - -Execute the full `/choose-schema` skill logic. - -**Outcome saved to session:** -- `provider` — the provider name (e.g., `bamboohr`) -- `schema` — the target category or `custom` -- `schema_source` — `builtin`, `imported`, or `custom` -- `resources` — list of resources to build -- `schema_fields` — field definitions (for custom/imported schemas) -- `schema_file` — path to per-category schema reference (for builtin) - ---- - -## Step 2 — Check Connector - -Execute the full `/check-connector` skill logic. - -**Outcome saved to session:** -- `cli_available` — whether the StackOne CLI is usable -- `connector_exists` — whether a base config was pulled from the index -- `connector_path` — where the config lives -- `auth_type` — the authentication method - ---- - -## Step 3 — Scope Actions - -Execute the full `/scope-actions` skill logic. - -**Outcome saved to session:** -- `action_scope` — `{ resource: [actions] }` map -- `use_case` — builder's description of their goal -- `known_limitations` — any flagged gaps - ---- - -## Step 4 — Map Fields - -Execute the full `/map-fields` skill logic. - -**Outcome:** Partial YAML files written to `src/configs/{{provider}}/` - ---- - -## Step 5 — Validate - -Execute the full `/validate-connector` skill logic. - -**Outcome saved to session:** -- `validated: true` - ---- - -## Step 6 — Test - -Execute the full `/test-connector` skill logic. - -**Outcome saved to session:** -- `tested: true` -- `completed_at` - ---- - -## Session File Schema - -`.connector-build-session.json` lives at the project root and carries all state between steps: - -```json -{ - "provider": "bamboohr", - "schema": "hris", - "schema_source": "builtin", - "schema_file": "references/schemas/hris.md", - "schema_fields": [], - "resources": ["employees", "time_off"], - "cli_available": true, - "connector_exists": false, - "connector_path": "src/configs/bamboohr", - "auth_type": "api_key", - "action_scope": { - "employees": ["list", "get"], - "time_off": ["list", "get", "create"] - }, - "use_case": "Sync HR data into customer's internal system", - "known_limitations": [], - "validated": false, - "tested": false, - "session_step": "map-fields", - "completed_at": null -} -``` - ---- - -## Rules - -- Do not write YAML files until Step 4 (`map-fields`) -- Do not run live tests until Step 5 (`validate-connector`) has passed -- Do not delete the session file unless the builder explicitly requests a reset -- Always read session context at the start of each step — never ask for information already in the session -``` - -**Step 3: Verify** -```bash -head -5 /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/stackone-unified-builder/SKILL.md -``` -Expected: frontmatter shows `name: stackone-unified-builder` and `invoke: build-unified-connector`. - ---- - -### Task 3: Add MCP tool references to unified builder sub-skills - -**Files:** -- Modify: `.claude/plugins/stackone-unified-builder/skills/check-connector/SKILL.md` -- Modify: `.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md` -- Modify: `.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md` - -**Step 1: Update check-connector — add S3 index check before CLI** - -In `check-connector/SKILL.md`, after the "Session File" section and before "Step 1: Detect CLI availability", insert: - -```markdown -## Step 0: Check StackOne provider index - -Before checking the CLI, look up the provider in StackOne's index: - -``` -map_provider_key("{{provider}}") -``` - -If a provider key is returned, run: - -``` -get_provider_actions("{{provider_key}}") -``` - -If indexed actions exist, tell the builder: -> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference when building your connector." - -Save the provider key to session as `provider_key`. Continue to CLI check below. -``` - -**Step 2: Update map-fields — add vector_search for field coverage** - -In `map-fields/SKILL.md`, after the "Session File" section, insert: - -```markdown -## Step 0: Check StackOne action coverage - -Before mapping fields, check whether StackOne has coverage data for this provider: - -``` -get_provider_coverage("{{provider_key}}") -``` - -If coverage data exists, use it to pre-populate likely field paths and flag any known gaps. - -Also run a vector search to find similar connectors as reference: - -``` -vector_search("{{schema}} {{provider}} field mapping") -``` - -Use any matching results to inform the fieldConfigs you build. -``` - -**Step 3: Update test-connector — add test_actions and scramble_credentials** - -In `test-connector/SKILL.md`, replace the section "If CLI is available:" for running tests with: - -```markdown -**If CLI is available and MCP tools are accessible:** - -Use `test_actions` for automated testing: -``` -test_actions({ - provider: "{{provider}}", - actions: ["unified_list_{{resource}}", "unified_get_{{resource}}"] -}) -``` - -Poll with `get_test_actions_task_status(taskId)` until complete. - -**If running manually via CLI:** -```bash -npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} -``` - -**After all tests pass, always run:** -``` -scramble_credentials("{{provider}}") -``` -``` - -**Step 4: Verify all three files were updated** -```bash -grep -l "map_provider_key\|vector_search\|test_actions\|scramble_credentials" \ - /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/*/SKILL.md -``` -Expected: 3 files listed. - ---- - -### Task 4: Scaffold new stackone-connector-builder plugin - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/.claude-plugin/plugin.json` -- Create all skill directories - -**Step 1: Create directories** - -```bash -BASE="/Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder" -mkdir -p "$BASE/.claude-plugin" -mkdir -p "$BASE/skills/stackone-connector-builder" -mkdir -p "$BASE/skills/setup-connector" -mkdir -p "$BASE/skills/configure-auth" -mkdir -p "$BASE/skills/discover-actions" -mkdir -p "$BASE/skills/build-config" -mkdir -p "$BASE/skills/validate-connector" -mkdir -p "$BASE/skills/test-connector" -mkdir -p "$BASE/references" -``` - -**Step 2: Write plugin.json** - -```json -{ - "name": "stackone-connector-builder", - "version": "1.0.0", - "description": "Interactive wizard for building generic Falcon connectors. Guides builders through provider setup, authentication, action discovery (scoped or maximal), config generation, validation, and testing with full cleanup.", - "author": { - "name": "StackOne", - "email": "engineering@stackone.com" - }, - "license": "MIT", - "keywords": ["connector", "falcon", "generic", "stackone", "builder", "discovery"] -} -``` - -**Step 3: Copy shared references from unified builder** - -```bash -BASE="/Users/cameroncarlin/Projects/connectors-template/.claude/plugins" -cp "$BASE/stackone-unified-builder/references/connector-patterns.md" \ - "$BASE/stackone-connector-builder/references/" -cp "$BASE/stackone-unified-builder/references/cli-commands.md" \ - "$BASE/stackone-connector-builder/references/" -``` - -**Step 4: Verify** -```bash -ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder/ -``` -Expected: `.claude-plugin/`, `skills/`, `references/` present. - ---- - -### Task 5: Write auth-patterns.md reference - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/references/auth-patterns.md` - -**Step 1: Write the file** - -```markdown -# Authentication Patterns Reference - -Detailed YAML patterns for every authentication type supported by the Falcon framework. -Used by the `configure-auth` sub-skill. - -## Supported Auth Types - -| Type | When to use | -|------|-------------| -| `custom` (API Key / Bearer) | Static token or key passed in header or query param | -| `custom` (Basic Auth) | Username + password base64-encoded in Authorization header | -| `oauth2` | User-facing OAuth 2.0 authorization code flow | -| `custom` (Client Credentials) | Machine-to-machine OAuth 2.0 with client_id + client_secret | -| `custom` (Custom Headers) | Any combination of static headers | - -> **Note:** Falcon only supports `type: custom` and `type: oauth2`. All non-OAuth flows use `type: custom` with appropriate args and headers. - ---- - -## API Key / Bearer Token - -```yaml -authentication: - type: custom - args: - api_key: - value: "${PROVIDER_API_KEY}" - headers: - Authorization: "Bearer ${args.api_key.value}" -baseUrl: "https://api.provider.com/v1" -``` - -**`.env` variable:** `PROVIDER_API_KEY=your_key_here` - ---- - -## API Key in Query Parameter - -```yaml -authentication: - type: custom - args: - api_key: - value: "${PROVIDER_API_KEY}" -baseUrl: "https://api.provider.com/v1" -# Pass as query param in each action's entrypointUrl: -# entrypointUrl: "/employees?api_key=${args.api_key.value}" -``` - ---- - -## Basic Auth (Username + Password) - -```yaml -authentication: - type: custom - args: - username: - value: "${PROVIDER_USERNAME}" - password: - value: "${PROVIDER_PASSWORD}" - headers: - Authorization: "Basic ${base64(args.username.value + ':' + args.password.value)}" -baseUrl: "https://api.provider.com/v1" -``` - -**`.env` variables:** `PROVIDER_USERNAME=user`, `PROVIDER_PASSWORD=pass` - ---- - -## OAuth 2.0 (Authorization Code Flow) - -```yaml -authentication: - type: oauth2 - clientId: "${PROVIDER_CLIENT_ID}" - clientSecret: "${PROVIDER_CLIENT_SECRET}" - tokenUrl: "https://auth.provider.com/oauth/token" - authorizationUrl: "https://auth.provider.com/oauth/authorize" - scopes: - - read:employees - - read:departments -baseUrl: "https://api.provider.com/v1" -``` - -**`.env` variables:** `PROVIDER_CLIENT_ID=id`, `PROVIDER_CLIENT_SECRET=secret` - ---- - -## Client Credentials (Machine-to-Machine OAuth) - -```yaml -authentication: - type: custom - args: - access_token: - value: "${PROVIDER_ACCESS_TOKEN}" - headers: - Authorization: "Bearer ${args.access_token.value}" -baseUrl: "https://api.provider.com/v1" -``` - -Obtain `access_token` via a separate token exchange step outside the connector. - -**`.env` variable:** `PROVIDER_ACCESS_TOKEN=token` - ---- - -## Multiple Headers - -```yaml -authentication: - type: custom - args: - api_key: - value: "${PROVIDER_API_KEY}" - account_id: - value: "${PROVIDER_ACCOUNT_ID}" - headers: - X-API-Key: "${args.api_key.value}" - X-Account-Id: "${args.account_id.value}" - Content-Type: "application/json" -baseUrl: "https://api.provider.com/v1" -``` - ---- - -## Credential Variable Naming Convention - -Variables in `.env` follow: `_` - -| Provider | Credential | Variable name | -|----------|-----------|--------------| -| BambooHR | API Key | `BAMBOOHR_API_KEY` | -| Workday | Client ID | `WORKDAY_CLIENT_ID` | -| Salesforce | Access Token | `SALESFORCE_ACCESS_TOKEN` | -| HiBob | Username | `HIBOB_USERNAME` | - ---- - -## Common Auth Errors - -| Error | Cause | Fix | -|-------|-------|-----| -| `401 Unauthorized` | Wrong credentials or expired token | Check `.env` values, re-authenticate | -| `403 Forbidden` | Valid credentials, insufficient permissions | Add required scopes or API permissions | -| `${args.api_key.value}` appears literally in request | Interpolation not working | Ensure `args` block nests under `authentication` | -| `base64 is not defined` | Basic auth YAML error | Use the literal string format shown above | -``` - -**Step 2: Verify** -```bash -wc -l /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder/references/auth-patterns.md -``` -Expected: 100+ lines. - ---- - -### Task 6: Write orchestrator SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: stackone-connector-builder -description: Interactive wizard for building a generic Falcon connector. Guides builders through provider setup, authentication, action discovery (scoped or maximal), YAML config generation, validation, and live testing with cleanup. Auto-triggers when someone asks to build a new connector, add a new provider, or create a Falcon config. -invoke: build-connector ---- - -# Build Connector - -End-to-end wizard for building a generic StackOne Falcon connector. -Use this when you want to expose a provider's raw API responses — no schema mapping required. -For connectors that normalise data to a standard schema, use `/build-unified-connector` instead. - -## Quick Reference - -| Step | Command | What it does | -|------|---------|-------------| -| 1 | `/setup-connector` | Provider name, index check, CLI pull or scaffold | -| 2 | `/configure-auth` | Set up authentication (API key, OAuth2, Basic Auth) | -| 3 | `/discover-actions` | Choose scoped actions or discover everything | -| 4 | `/build-config` | Generate YAML for all confirmed actions | -| 5 | `/validate-connector` | Validate the YAML config | -| 6 | `/test-connector` | Live test + clean up all test records | - -Progress is saved to `.connector-build-session.json` — pause and resume any time. - ---- - -## Starting the Wizard - -Check for an existing `.connector-build-session.json`. If found: -> "Found an existing session: -> - **Provider:** `{{provider}}` -> - **Last step:** `{{session_step}}` -> -> Resume or restart?" - -If no session, greet: -> "Welcome to the StackOne connector builder. I'll help you build a Falcon connector that exposes `{{provider}}`'s API through StackOne. -> -> If you want to map the data to a standard schema (HRIS, ATS, CRM, etc.), use `/build-unified-connector` instead." - ---- - -## Steps - -### Step 1 — Setup -Execute `/setup-connector` logic. Saves: `provider`, `provider_key`, `cli_available`, `connector_exists`, `connector_path`. - -### Step 2 — Configure Auth -Execute `/configure-auth` logic. Saves: `auth_type`. Writes auth block to connector YAML. - -### Step 3 — Discover Actions -Execute `/discover-actions` logic. Saves: `discovery_mode`, `action_scope`, `use_case`. - -### Step 4 — Build Config -Execute `/build-config` logic. Writes action YAML to `src/configs/{{provider}}/`. - -### Step 5 — Validate -Execute `/validate-connector` logic. Saves: `validated: true`. - -### Step 6 — Test -Execute `/test-connector` logic. Saves: `tested: true`, `test_artifacts`, `completed_at`. - ---- - -## Session File Schema - -```json -{ - "provider": "workday", - "provider_key": "workday", - "connector_path": "src/configs/workday", - "cli_available": true, - "connector_exists": false, - "auth_type": "oauth2", - "discovery_mode": "scoped", - "action_scope": { - "employees": ["list", "get", "create"], - "departments": ["list", "get"] - }, - "use_case": "Read employee and department data", - "known_limitations": [], - "test_artifacts": [ - { "resource": "employees", "id": "EMP_test_001", "cleaned_up": true }, - { "resource": "departments", "id": "DEP_test_007", "cleaned_up": false, "reason": "no delete endpoint" } - ], - "validated": false, - "tested": false, - "session_step": "build-config", - "completed_at": null -} -``` - ---- - -## Rules - -- Do not write YAML until Step 4 (`build-config`) -- Do not run tests until Step 5 (`validate-connector`) passes -- Always clean up test records — log anything that cannot be removed -- Always run `scramble_credentials` after testing -- Never ask for information already in the session -``` - ---- - -### Task 7: Write setup-connector SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: setup-connector -description: Step 1 of building a generic Falcon connector. Checks StackOne's provider index, detects CLI availability, and either pulls an existing connector base config or scaffolds a new one. -invoke: setup-connector ---- - -# Setup Connector - -Step 1 of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. If `provider` is already set: -> "Resuming — provider already set to `{{provider}}`. Run `/configure-auth` to continue." - ---- - -## Step 1: Ask for provider name - -Ask: -> "What provider are you building a connector for? (e.g., `workday`, `bamboohr`, `rippling`)" - -Save as `provider` (lowercase, hyphenated). - ---- - -## Step 2: Check StackOne provider index - -``` -map_provider_key("{{provider}}") -``` - -If a key is returned, save as `provider_key`. - -``` -get_provider_actions("{{provider_key}}") -``` - -If indexed actions exist: -> "Found `{{provider}}` in the StackOne index with {{N}} known actions. I'll use this as a reference." - -If not found: -> "`{{provider}}` isn't indexed yet — we'll discover actions from scratch in Step 3." - ---- - -## Step 3: Detect CLI availability - -```bash -npx @stackone/cli --version -``` - -Save `cli_available: true/false`. - -If unavailable: -> "StackOne CLI isn't available. I'll guide manual scaffolding instead." - ---- - -## Step 4: Pull or scaffold - -**If CLI available and provider exists in index:** -```bash -npx @stackone/cli pull {{provider}} -``` -Verify `src/configs/{{provider}}/` was created. Save `connector_exists: true`. - -**If scaffolding from scratch:** -```bash -npx @stackone/cli scaffold {{provider}} -``` -Or if CLI unavailable, create `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` manually: -```yaml -name: {{provider}} -version: "1.0" -authentication: {} # configured in next step -baseUrl: "" # set in next step -actions: [] -``` -Save `connector_exists: false`. - -Save `connector_path: "src/configs/{{provider}}"`. - ---- - -## Handoff - -> "Setup complete. ✓ Connector at `src/configs/{{provider}}/` -> -> Next: configure authentication. -> Run `/configure-auth` to continue." - -Update `session_step` to `"configure-auth"`. -``` - ---- - -### Task 8: Write configure-auth SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/configure-auth/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: configure-auth -description: Step 2 of building a generic Falcon connector. Asks the builder which authentication type the provider uses, generates the correct YAML auth block, and writes it into the connector config file. -invoke: configure-auth ---- - -# Configure Auth - -Step 2 of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. Confirm: -> "Configuring auth for `{{provider}}`." - -Read `${CLAUDE_PLUGIN_ROOT}/references/auth-patterns.md` before starting. - ---- - -## Step 1: Identify auth type - -Ask: -> "How does `{{provider}}`'s API authenticate requests? -> -> **A) API Key / Bearer token** — static key passed in a header -> **B) OAuth 2.0** — users authorise via a browser flow -> **C) Basic Auth** — username and password -> **D) API Key in query parameter** — key appended to the URL -> **E) Something else** — describe it and I'll help figure it out -> -> (A/B/C/D/E)" - ---- - -## Step 2: Collect credentials - -Ask for the specific values needed: - -- **A / D (API Key):** "What's the header or query param name? (e.g., `Authorization`, `X-API-Key`, `api_key`)" -- **B (OAuth2):** "Do you have the token URL and authorization URL? What scopes are needed?" -- **C (Basic Auth):** "Is this username + password, or username + API key as password?" -- **E (Other):** Gather details and match to the closest pattern in `auth-patterns.md` - ---- - -## Step 3: Write auth block into connector YAML - -Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. - -Replace the `authentication: {}` placeholder (or existing auth block) with the correct pattern from `auth-patterns.md`. - -Also set `baseUrl` if the builder knows it: -> "What is the base URL for `{{provider}}`'s API? (e.g., `https://api.provider.com/v1`)" - -If unknown, leave a placeholder: `baseUrl: "# TODO: set provider base URL"`. - ---- - -## Step 4: Show required .env variables - -Tell the builder exactly which variables to add to `.env`: -> "Add these to your `.env` file before testing: -> ``` -> {{PROVIDER_CREDENTIAL_VARS}} -> ```" - ---- - -## Step 5: Confirm - -Show the written auth block and ask: -> "Does this look right? The credentials stay in `.env` — the YAML only references them by variable name." - -Apply any corrections. - -Save `auth_type` to session. - ---- - -## Handoff - -> "Auth configured. ✓ -> -> Next: choose which actions to build. -> Run `/discover-actions` to continue." - -Update `session_step` to `"discover-actions"`. -``` - ---- - -### Task 9: Write discover-actions SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/discover-actions/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: discover-actions -description: Step 3 of building a generic Falcon connector. Lets the builder choose between scoped discovery (use-case driven) or maximal discovery (finds every available API endpoint using the StackOne discover_actions MCP tool). Saves the confirmed action scope to session. -invoke: discover-actions ---- - -# Discover Actions - -Step 3 of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. Confirm: -> "Discovering actions for `{{provider}}`." - ---- - -## Step 1: Choose discovery mode - -Ask: -> "How much of `{{provider}}`'s API do you want to cover? -> -> **A) Scoped** — Tell me your use case and I'll recommend the right actions -> **B) Maximal** — Discover every available endpoint (takes 5–15 minutes, uses autonomous research) -> -> (A/B)" - -Save `discovery_mode: "scoped"` or `"maximal"`. - ---- - -## Path A: Scoped discovery - -### A1: Ask about use case - -> "Describe what you need this connector to do. Examples: -> - "Read employee and department data into our platform" -> - "Let users create and update time-off requests" -> - "Sync job postings to our job board" -> -> What's your use case?" - -Save `use_case` to session. - -### A2: Look up relevant actions - -Run a vector search to find related actions: -``` -vector_search("{{use_case}} {{provider}}") -``` - -Also check StackOne's known actions for this provider: -``` -get_provider_actions("{{provider_key}}") -``` - -### A3: Present recommended action set - -Based on the use case and search results, present a recommended scope: - -> "Based on your use case, here are the actions I recommend: -> -> | Resource | Actions | Endpoints | -> |----------|---------|----------| -> | `employees` | list, get | `GET /employees`, `GET /employees/{id}` | -> | `departments` | list | `GET /departments` | -> -> Does this cover what you need? You can add or remove anything." - -Allow the builder to adjust freely. - -### A4: Flag known limitations - -If `get_provider_actions` returned coverage data, note any gaps: -> "Note: `{{provider}}` doesn't appear to support `create` on employees via API — only `list` and `get` are confirmed." - -### A5: Save to session - -```json -{ - "discovery_mode": "scoped", - "action_scope": { - "employees": ["list", "get"], - "departments": ["list"] - }, - "use_case": "Read employee and department data" -} -``` - ---- - -## Path B: Maximal discovery - -### B1: Check S3 cache first - -``` -map_provider_key("{{provider}}") -get_provider_actions("{{provider_key}}") -``` - -If cached data exists with many actions: -> "Found {{N}} actions in the StackOne index for `{{provider}}`. I can use this data directly — no need to wait for a full discovery run." - -Use cached data and skip to B4. - -### B2: Launch autonomous discovery - -``` -discover_actions({ provider: "{{provider}}", maxIterations: 30 }) -``` - -Returns a `taskId` immediately. Tell the builder: -> "Launched autonomous discovery for `{{provider}}`. This typically takes 5–15 minutes. I'll poll for results — you can continue working in another tab." - -### B3: Poll for completion - -Every 60–90 seconds: -``` -get_discover_actions_task_status("{{taskId}}", "{{provider}}") -``` - -Status: `pending` → `running` → `complete` - -When `running`, report progress: -> "Still discovering... found {{N}} actions so far." - -### B4: Run version analysis - -Once discovery completes, extract endpoints and run: -``` -analyze_versioning({ provider: "{{provider}}", endpoints: [...], maxIterations: 5 }) -``` - -Poll with `get_analyze_versioning_task_status`. This identifies deprecated endpoints and version conflicts. - -### B5: Present full action list - -> "Discovery complete. Found {{N}} actions across {{M}} resources: -> -> | Resource | Actions found | -> |----------|-------------| -> | `employees` | list, get, create, update | -> | `departments` | list, get | -> | `time_off` | list, get, create, delete | -> | ... | -> -> Which would you like to include? (all / select specific ones)" - -### B6: Save to session - -```json -{ - "discovery_mode": "maximal", - "action_scope": { ... all confirmed actions ... }, - "use_case": "Full API coverage" -} -``` - ---- - -## Handoff - -> "Action scope confirmed. ✓ -> -> {{N}} actions across {{M}} resources. -> -> Next: generate the connector YAML. -> Run `/build-config` to continue." - -Update `session_step` to `"build-config"`. -``` - ---- - -### Task 10: Write build-config SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: build-config -description: Step 4 of building a generic Falcon connector. Generates YAML action configurations for all confirmed actions using actionType custom, writes partial files for each resource, and updates the main connector YAML with $ref links. -invoke: build-config ---- - -# Build Config - -Step 4 of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. Confirm: -> "Building config for `{{provider}}` — {{N}} actions across {{M}} resources." - -Read `${CLAUDE_PLUGIN_ROOT}/references/connector-patterns.md` before writing any YAML. - -If `action_scope` is missing: -> "No action scope found. Run `/discover-actions` first." - ---- - -## Step 1: Ask for endpoint details (if not already known) - -If endpoint URLs weren't captured during discovery, ask for each resource: -> "What is the API endpoint for `{{resource}}`? (e.g., `/employees`, `/v2/employees`)" - -Use provider API docs if the builder can share a link, or reference any data from `get_provider_actions`. - ---- - -## Step 2: For each resource, write a partial YAML file - -Create `src/configs/{{provider}}/{{provider}}.{{resource}}.s1.partial.yaml`. - -Use the non-unified action pattern from `connector-patterns.md`. Each action should be: - -```yaml -- name: list_{{resource}} - actionType: custom - entrypointUrl: "/{{resource}}" - entrypointHttpMethod: GET - inputs: - page: - type: number - required: false - per_page: - type: number - required: false - steps: - - type: request - id: fetch_{{resource}} -``` - -For `get` actions: -```yaml -- name: get_{{resource}} - actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: GET - inputs: - id: - type: string - required: true - steps: - - type: request - id: fetch_{{resource}} -``` - -For `create` actions: -```yaml -- name: create_{{resource}} - actionType: custom - entrypointUrl: "/{{resource}}" - entrypointHttpMethod: POST - inputs: - body: - type: object - required: true - steps: - - type: request - id: create_{{resource}} -``` - -For `update` actions: -```yaml -- name: update_{{resource}} - actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: PATCH - inputs: - id: - type: string - required: true - body: - type: object - required: true - steps: - - type: request - id: update_{{resource}} -``` - -For `delete` actions: -```yaml -- name: delete_{{resource}} - actionType: custom - entrypointUrl: "/{{resource}}/${inputs.id}" - entrypointHttpMethod: DELETE - inputs: - id: - type: string - required: true - steps: - - type: request - id: delete_{{resource}} -``` - ---- - -## Step 3: Update main connector YAML with $refs - -Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. - -Add a `$ref` for each partial under `actions`: -```yaml -actions: - - $ref: "./{{provider}}.employees.s1.partial.yaml" - - $ref: "./{{provider}}.departments.s1.partial.yaml" -``` - ---- - -## Step 4: Review with builder - -Show the file list created: -> "Config written: -> - `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` (updated) -> - `src/configs/{{provider}}/{{provider}}.employees.s1.partial.yaml` -> - ..." - -Ask: -> "Do the endpoint URLs and HTTP methods look right? Any actions need adjusting before we validate?" - -Apply corrections. - ---- - -## Handoff - -> "Config built. ✓ -> -> Next: validate the YAML. -> Run `/validate-connector` to continue." - -Update `session_step` to `"validate-connector"`. -``` - ---- - -### Task 11: Write generic validate-connector SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md` - -**Step 1: Write the file** - -This is simpler than the unified version — no `schemaType`, `fieldConfigs`, or `map_fields` steps to check. - -```markdown ---- -name: validate-connector -description: Step 5 of building a generic Falcon connector. Runs stackone validate, interprets errors, guides fixes, and confirms the config is structurally correct before live testing. -invoke: validate-connector ---- - -# Validate Connector - -Step 5 of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. Confirm: -> "Validating `src/configs/{{provider}}/`" - -Read `${CLAUDE_PLUGIN_ROOT}/references/cli-commands.md` for the error table. - ---- - -## Step 1: Run validation - -**If `cli_available` is true:** -```bash -npx @stackone/cli validate src/configs/{{provider}}/{{provider}}.connector.s1.yaml -``` - -**If CLI unavailable (manual review):** - -Read each `.yaml` and `.partial.yaml` file and check: -- Every action has `entrypointUrl` and `entrypointHttpMethod` -- Valid `actionType` values: `custom`, `list`, `get`, `create`, `update`, `delete` -- Actions that take an ID have `${inputs.id}` in the URL and `id` in `inputs` -- `create` and `update` actions have a `body` input -- All `$ref` paths in the main connector point to files that exist -- No tabs — 2-space indentation throughout -- `baseUrl` is set (not a placeholder) -- `authentication` block is populated (not `{}`) - ---- - -## Step 2: Fix errors - -For each error, explain and fix: - -**Missing `entrypointUrl`:** -```yaml -entrypointUrl: "/{{resource}}" -entrypointHttpMethod: GET -``` - -**Wrong `actionType`:** -> Valid: `custom`, `list`, `get`, `create`, `update`, `delete` - -**Missing `inputs.id` for get/update/delete:** -```yaml -inputs: - id: - type: string - required: true -``` - -**Bad `$ref` path:** -> Check filename: `{{provider}}.{{resource}}.s1.partial.yaml` - -**Empty auth block:** -> Run `/configure-auth` to set up authentication. - -Re-run after each fix. Repeat until clean. - ---- - -## Step 3: Confirm - -> "Validation passed. ✓ -> -> - Provider: `{{provider}}` -> - Resources: {{resources}} -> - Actions: {{action_scope}}" - -Save `validated: true` to session. - ---- - -## Handoff - -> "Ready for testing. Ensure your `.env` credentials are set. -> -> Run `/test-connector` to continue." - -Update `session_step` to `"test-connector"`. -``` - ---- - -### Task 12: Write generic test-connector SKILL.md - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/skills/test-connector/SKILL.md` - -**Step 1: Write the file** - -```markdown ---- -name: test-connector -description: Step 6 (final) of building a generic Falcon connector. Tests every action live against the provider API, verifies responses, cleans up all test records created during testing, and produces a cleanup report. -invoke: test-connector ---- - -# Test Connector - -Step 6 (final step) of the generic connector build process. - -## Session File - -Read `.connector-build-session.json`. - -If `validated` is not `true`: -> "Connector hasn't been validated yet. Run `/validate-connector` first." - -Initialise `test_artifacts: []` in session if not present. - ---- - -## Step 1: Confirm credentials - -Ask: -> "Are your `.env` credentials ready for `{{provider}}`? -> You'll need: `{{PROVIDER_CREDENTIAL_VARS}}`" - -Wait for confirmation. - ---- - -## Step 2: Test each action - -Test in this order for each resource: `list` → `get` → `create` → `update` → `delete`. - -**If MCP tools are accessible:** -``` -test_actions({ - provider: "{{provider}}", - actions: ["list_{{resource}}", "get_{{resource}}"] -}) -``` -Poll with `get_test_actions_task_status(taskId)`. - -**If running manually:** -```bash -npx @stackone/cli test {{provider}} list_{{resource}} -npx @stackone/cli test {{provider}} get_{{resource}} --input '{"id": "test-id"}' -``` - ---- - -## Step 3: Track test artifacts - -Every time a `create` action succeeds, immediately record the new record: - -```json -{ "resource": "{{resource}}", "id": "{{returned_id}}", "cleaned_up": false } -``` - -Add to `test_artifacts` in session. - ---- - -## Step 4: Verify responses - -**`list` actions:** -- Response is an array or has a `data` array -- At least one record returned (or empty array with no error) - -**`get` actions:** -- Single object returned -- Object has an `id` field - -**`create` actions:** -- Record created (2xx response) -- Response contains the new record's `id` - -**`update` actions:** -- Changes reflected in response or confirmed with follow-up `get` - -**`delete` actions:** -- 200, 202, or 204 response -- Follow-up `get` returns 404 or empty - ---- - -## Step 5: Clean up after every create - -After verifying each `create` + `update`: - -**If `delete` is in scope for this resource:** -```bash -npx @stackone/cli test {{provider}} delete_{{resource}} --input '{"id": "{{test_record_id}}"}' -``` - -Verify with a follow-up `get` (expect 404). - -Mark in session: `"cleaned_up": true`. - -**If `delete` is NOT in scope:** - -Attempt soft cleanup in this order: -1. Look for an `update` action — set status to `inactive`, `archived`, or `cancelled` -2. Look for a `cancel` or `deactivate` action -3. If none available: log the record as irremovable - -Mark in session: `"cleaned_up": false`, `"reason": "no delete endpoint"`. - ---- - -## Step 6: Diagnose failures - -**401/403 — Auth error:** -> "Authentication failed. Check your `.env` credentials and the `authentication` block in the connector YAML." - -**404 — Wrong URL:** -> "Endpoint not found. Verify `entrypointUrl` matches the provider's API docs." - -**Empty response / wrong shape:** -> "The response structure doesn't match expectations. Use `--debug` to inspect the raw response and update `entrypointUrl` or HTTP method if needed." - -**Create succeeds but update/delete fails:** -> "The record was created but subsequent operations failed — likely the ID format differs between create response and path parameter. Check `${inputs.id}` matches the ID field in the create response." - -Fix the issue in the partial YAML and re-run the affected action. Re-clean up any leftover records. - ---- - -## Step 7: Scramble credentials - -After all tests pass (or are resolved): - -``` -scramble_credentials("{{provider}}") -``` - -This removes real credentials from the config history. - ---- - -## Step 8: Cleanup report + final summary - -> "All tests complete. -> -> **Test cleanup summary:** -> | Resource | Record ID | Status | -> |----------|-----------|--------| -> | `employees` | EMP_test_001 | ✓ deleted | -> | `departments` | DEP_test_007 | ⚠ not deleted — no delete endpoint | -> -> **Actions verified:** -> | Resource | list | get | create | update | delete | -> |----------|------|-----|--------|--------|--------| -> | `employees` | ✓ | ✓ | ✓ | ✓ | ✓ | -> | `departments` | ✓ | ✓ | — | — | — | -> -> **Next steps:** -> 1. Commit: `git add src/configs/{{provider}}/ && git commit -m 'feat: add {{provider}} connector'` -> 2. Address any undeleted test records manually (IDs listed above) -> 3. Open a PR to the connectors repository" - -Save `tested: true`, `completed_at: "{{datetime}}"` to session. -``` - ---- - -### Task 13: Write README.md for generic plugin - -**Files:** -- Create: `.claude/plugins/stackone-connector-builder/README.md` - -**Step 1: Write the file** - -```markdown -# stackone-connector-builder - -Interactive wizard for building generic Falcon connectors. Guides builders through provider setup, authentication, action discovery, YAML config generation, validation, and live testing — with full test record cleanup. - -For connectors that normalise data to a standard schema (HRIS, ATS, CRM, etc.), use `stackone-unified-builder` instead. - -## Usage - -### Full wizard - -```bash -/build-connector -``` - -### Individual steps - -| Command | Step | -|---------|------| -| `/setup-connector` | 1. Provider setup and scaffold | -| `/configure-auth` | 2. Authentication configuration | -| `/discover-actions` | 3. Scoped or maximal action discovery | -| `/build-config` | 4. Generate action YAML | -| `/validate-connector` | 5. Validate config structure | -| `/test-connector` | 6. Live test + cleanup | - -## Discovery modes - -**Scoped** — Describe your use case, get a focused action set. Fast. - -**Maximal** — Autonomous discovery of every available API endpoint using StackOne's `discover_actions` tool. Takes 5–15 minutes but finds everything. - -## Test cleanup - -`/test-connector` records every ID created during testing and deletes them after verification. A cleanup report is shown at the end listing anything that couldn't be removed. - -## Requirements - -- `connectors-template` project structure -- Node.js (for StackOne CLI — optional but recommended) -- StackOne MCP tools (for `discover_actions`, `test_actions`, etc.) -- Provider API credentials in `.env` -``` - ---- - -### Task 14: Final verification - -**Step 1: Confirm unified builder rename is complete** -```bash -ls /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-unified-builder/skills/ -``` -Expected: `stackone-unified-builder/` folder present (not `stackone-connector-builder/`). - -**Step 2: Confirm new generic plugin structure** -```bash -find /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/stackone-connector-builder -type f | sort -``` - -Expected output: -``` -.claude-plugin/plugin.json -README.md -references/auth-patterns.md -references/cli-commands.md -references/connector-patterns.md -skills/stackone-connector-builder/SKILL.md -skills/setup-connector/SKILL.md -skills/configure-auth/SKILL.md -skills/discover-actions/SKILL.md -skills/build-config/SKILL.md -skills/validate-connector/SKILL.md -skills/test-connector/SKILL.md -``` - -**Step 3: Confirm no invoke command conflicts** -```bash -grep -r "^invoke:" /Users/cameroncarlin/Projects/connectors-template/.claude/plugins/*/skills/*/SKILL.md | sort -``` -Expected: `/build-connector` appears once, `/build-unified-connector` appears once. All others unique. - -**Step 4: Done** — notify user. From 1fb069eb5e68868755903b19ca3739a133488f77 Mon Sep 17 00:00:00 2001 From: Cameron Carlin Date: Mon, 23 Mar 2026 11:24:28 +0000 Subject: [PATCH 09/13] Delete docs/plans directory --- ...3-20-connector-builder-plugins-redesign.md | 128 ------------------ 1 file changed, 128 deletions(-) delete mode 100644 docs/plans/2026-03-20-connector-builder-plugins-redesign.md diff --git a/docs/plans/2026-03-20-connector-builder-plugins-redesign.md b/docs/plans/2026-03-20-connector-builder-plugins-redesign.md deleted file mode 100644 index c5ddf79..0000000 --- a/docs/plans/2026-03-20-connector-builder-plugins-redesign.md +++ /dev/null @@ -1,128 +0,0 @@ -# Connector Builder Plugins Redesign - -## Goal - -Rename the existing unified connector builder plugin to `stackone-unified-builder` (invoke: `/build-unified-connector`), and create a new sibling plugin `stackone-connector-builder` (invoke: `/build-connector`) for building generic (non-unified) Falcon connectors. - -## Architecture - -Both plugins live under `connectors-template/.claude/plugins/`, share the same session file pattern (`.connector-build-session.json`), and explicitly reference StackOne MCP tools. The unified builder maps provider data to a schema via `fieldConfigs`; the generic builder outputs raw provider responses via `actionType: custom`. - ---- - -## Part 1: Rename stackone-connector-builder → stackone-unified-builder - -### Changes required - -- Rename directory: `.claude/plugins/stackone-connector-builder/` → `.claude/plugins/stackone-unified-builder/` -- Update `plugin.json`: name, description, keywords -- Update main orchestrator `SKILL.md`: name, description, invoke command (`/build-unified-connector`) -- Add explicit MCP tool references to `check-connector`, `map-fields`, and `test-connector` skills where missing - -### MCP tools to add to unified builder - -| Skill | Tools to add | -|-------|-------------| -| `check-connector` | `map_provider_key`, `get_provider_actions` — check S3 index before CLI | -| `map-fields` | `get_stackone_actions`, `vector_search` — look up schema action coverage | -| `test-connector` | `test_actions`, `scramble_credentials` | - ---- - -## Part 2: New stackone-connector-builder (generic) - -### Plugin structure - -``` -.claude/plugins/stackone-connector-builder/ -├── .claude-plugin/ -│ └── plugin.json -├── README.md -├── skills/ -│ ├── stackone-connector-builder/ ← /build-connector (orchestrator) -│ ├── setup-connector/ ← /setup-connector (Step 1) -│ ├── configure-auth/ ← /configure-auth (Step 2) -│ ├── discover-actions/ ← /discover-actions (Step 3) -│ ├── build-config/ ← /build-config (Step 4) -│ ├── validate-connector/ ← /validate-connector (Step 5) -│ └── test-connector/ ← /test-connector (Step 6) -└── references/ - ├── connector-patterns.md ← symlink/copy from unified - ├── cli-commands.md ← symlink/copy from unified - └── auth-patterns.md ← new: detailed auth reference -``` - -### Steps - -| Step | Skill | Key behaviour | -|------|-------|--------------| -| 1 | `setup-connector` | Provider name → `map_provider_key` → `get_provider_actions` (S3 check) → CLI pull or scaffold | -| 2 | `configure-auth` | API key / OAuth2 / Basic Auth / custom; write auth block to connector YAML | -| 3 | `discover-actions` | Scoped (ask use case, recommend actions) OR maximal (`discover_actions` MCP, async 5–15 min) | -| 4 | `build-config` | Write YAML with all confirmed actions (`actionType: custom`, raw response, no fieldConfigs) | -| 5 | `validate-connector` | `stackone validate`; interpret and fix errors | -| 6 | `test-connector` | Test each action live; **clean up after itself** | - -### discover-actions detail - -**Scoped path:** -1. Ask use case in plain language -2. Run `get_stackone_actions` + `vector_search` to find relevant actions -3. Present recommended action set, let builder adjust -4. Save `action_scope` to session - -**Maximal path:** -1. Run `map_provider_key` to get exact provider key -2. Check `get_provider_actions` for cached data -3. If no cache: launch `discover_actions(provider, maxIterations: 30)` → get `taskId` -4. Poll `get_discover_actions_task_status` every 60–90s -5. Run `analyze_versioning` on discovered endpoints -6. Present full discovered action list for builder confirmation -7. Save `action_scope` to session - -### test-connector cleanup behaviour - -Test order: `list` → `get` → `create` → `update` → `delete` - -**Cleanup rules:** -- Record every `create` response and store the new record ID -- After `update` is verified, attempt `delete` on the test record -- After `delete`, confirm with a follow-up `get` (expect 404) -- If `delete` is not in scope: attempt soft cleanup (archive/deactivate/cancel if available) -- If no cleanup possible: log the record ID and warn the builder -- Use `scramble_credentials` after all tests pass - -**Cleanup report** (shown at end): -``` -Test cleanup summary: -✓ employees/EMP_test_001 — created and deleted -✓ time_off/TOF_test_042 — created and deleted -⚠ department/DEP_test_007 — created, no delete endpoint; record remains -``` - -### Session file schema (generic) - -```json -{ - "provider": "workday", - "connector_path": "src/configs/workday", - "cli_available": true, - "connector_exists": false, - "auth_type": "oauth2", - "discovery_mode": "scoped", - "action_scope": { - "employees": ["list", "get", "create"], - "departments": ["list", "get"] - }, - "use_case": "Read employee and department data", - "known_limitations": [], - "test_artifacts": [ - { "resource": "employees", "id": "EMP_test_001", "cleaned_up": true }, - { "resource": "departments", "id": "DEP_test_007", "cleaned_up": false, "reason": "no delete endpoint" } - ], - "validated": false, - "tested": false, - "session_step": "build-config", - "completed_at": null -} -``` From c73a92eaf68de73aeb60418568c679cd5a0c2817 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 11:37:20 +0000 Subject: [PATCH 10/13] fix: Splitting examples to be accurate --- .../bamboo-hr/bamboo-hr.connector.s1.yaml | 1 - .../bamboo-hr.employees.s1.partial.yaml | 616 ++--- .../breathehr/breathehr_v1-0-1.s1.yaml | 2412 ----------------- 3 files changed, 217 insertions(+), 2812 deletions(-) delete mode 100644 src/configs/breathehr/breathehr_v1-0-1.s1.yaml diff --git a/examples/bamboo-hr/bamboo-hr.connector.s1.yaml b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml index 7134b62..f37b661 100644 --- a/examples/bamboo-hr/bamboo-hr.connector.s1.yaml +++ b/examples/bamboo-hr/bamboo-hr.connector.s1.yaml @@ -1,5 +1,4 @@ StackOne: 1.0.0 -schemaType: unified info: title: BambooHR Unified key: bamboo-hr-unified diff --git a/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml index 0426fc1..7a8a7ec 100644 --- a/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml +++ b/examples/bamboo-hr/bamboo-hr.employees.s1.partial.yaml @@ -2,13 +2,10 @@ label: List Employees categories: - hris - actionType: list - description: Returns a paginated list of employees from BambooHR using the employee directory endpoint. - details: Lists all employees with basic unified schema fields including ID, name, contact information, and employee number. Uses BambooHR's directory endpoint which returns a subset of fields. For full field detail including date of birth, address, and employment history, use get_employee. + actionType: custom + description: Returns a list of employees from BambooHR using the employee directory endpoint. + details: Lists all employees from the BambooHR directory. Returns employee data including names, contact information, job title, department, and employment status. For full employee detail including date of birth, address, and custom fields, use get_employee. resources: https://documentation.bamboohr.com/reference/get-employees-list - entrypointUrl: /employees/directory - entrypointHttpMethod: get - steps: - stepId: get_employees description: Fetch all employees from BambooHR employee directory @@ -21,84 +18,31 @@ - name: Accept value: application/json in: headers - - - stepId: map_employees_data - description: Map employee directory fields to unified HRIS schema - stepFunction: - functionName: map_fields - version: '2' - parameters: - fields: - - targetFieldKey: id - expression: $.id - type: string - - targetFieldKey: first_name - expression: $.firstName - type: string - - targetFieldKey: last_name - expression: $.lastName - type: string - - targetFieldKey: personal_email - expression: $.homeEmail - type: string - - targetFieldKey: personal_phone_number - expression: $.mobilePhone - type: string - - targetFieldKey: employee_number - expression: $.employeeNumber - type: string - - targetFieldKey: title - expression: $.jobTitle - type: string - dataSource: $.steps.get_employees.output.data.employees - - - stepId: typecast_employees_data - description: Apply unified schema types to employee list data - stepFunction: - functionName: typecast - version: '2' - parameters: - fields: - - targetFieldKey: id - type: string - - targetFieldKey: first_name - type: string - - targetFieldKey: last_name - type: string - - targetFieldKey: personal_email - type: string - - targetFieldKey: personal_phone_number - type: string - - targetFieldKey: employee_number - type: string - - targetFieldKey: title - type: string - dataSource: $.steps.map_employees_data.output.data - result: - data: $.steps.typecast_employees_data.output.data + data: $.steps.get_employees.output.data - actionId: get_employee label: Get Employee categories: - hris - actionType: get - description: Returns a single employee record by ID with full unified HRIS schema field mapping, including bank details and employment history. - details: Makes three parallel-style API calls — main employee fields, directDeposit table for all bank accounts, and jobInfo table for full employment history — then combines them into a single unified response. Bank details and employment history are fetched as complete arrays in one call each. 404s on table endpoints are handled gracefully and return empty arrays. + actionType: custom + description: Returns a single employee record by ID with the requested fields. + details: Retrieves detailed information for a specific employee from BambooHR. Uses the fields query parameter to request specific BambooHR field names. Returns raw BambooHR field names in the response. resources: https://documentation.bamboohr.com/reference/get-employee - entrypointUrl: /employees/:id - entrypointHttpMethod: get - inputs: - name: id - description: The unique identifier of the employee to retrieve + description: The BambooHR employee ID type: string in: path required: true - + - name: fields + description: Comma-separated list of BambooHR field names to return (e.g. firstName,lastName,jobTitle,department,workEmail) + type: string + in: query + required: false steps: - stepId: fetch_employee - description: Fetch core employee fields from BambooHR + description: Fetch employee by ID from BambooHR stepFunction: functionName: request parameters: @@ -109,235 +53,101 @@ value: application/json in: headers - name: fields - value: id,firstName,lastName,dateOfBirth,homeEmail,terminationDate,mobilePhone,hireDate,address1,address2,city,zipCode,country,employmentHistoryStatus,employeeNumber,maritalStatus,jobTitle,ssn + value: $.inputs.fields in: query - - - stepId: fetch_bank_details - description: Fetch all bank account (direct deposit) records for the employee - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/tables/directDeposit - method: get - args: - - name: Accept - value: application/json - in: headers - customErrors: - - receivedStatus: 404 - targetStatus: 200 - message: No bank details found for this employee - - - stepId: fetch_employment_history - description: Fetch full employment history (job info) records for the employee - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/tables/jobInfo - method: get - args: - - name: Accept - value: application/json - in: headers - customErrors: - - receivedStatus: 404 - targetStatus: 200 - message: No employment history found for this employee - - - stepId: group_employee_data - description: Combine core employee data, bank details, and employment history into a single object - stepFunction: - functionName: group_data - parameters: - stepsDataToGroup: - - fetch_employee - - fetch_bank_details - - fetch_employment_history - isSingleRecord: true - - - stepId: map_employee_data - description: Map combined employee data to unified HRIS schema fields - stepFunction: - functionName: map_fields - version: '2' - parameters: - dataSource: $.steps.group_employee_data.output.data - fields: - - targetFieldKey: id - expression: $.fetch_employee.id - type: string - - targetFieldKey: first_name - expression: $.fetch_employee.firstName - type: string - - targetFieldKey: last_name - expression: $.fetch_employee.lastName - type: string - - targetFieldKey: date_of_birth - expression: $.fetch_employee.dateOfBirth - type: string - - targetFieldKey: personal_email - expression: $.fetch_employee.homeEmail - type: string - - targetFieldKey: termination_date - expression: $.fetch_employee.terminationDate - type: string - - targetFieldKey: personal_phone_number - expression: $.fetch_employee.mobilePhone - type: string - - targetFieldKey: start_date - expression: $.fetch_employee.hireDate - type: string - - targetFieldKey: home_location - type: object - expression: | - { - "street_1": $.fetch_employee.address1, - "street_2": $.fetch_employee.address2, - "city": $.fetch_employee.city, - "zip_code": $.fetch_employee.zipCode, - "country": $.fetch_employee.country - } - - targetFieldKey: employments - type: object - expression: $.fetch_employment_history.data - - targetFieldKey: employee_number - expression: $.fetch_employee.employeeNumber - type: string - - targetFieldKey: marital_status - expression: $.fetch_employee.maritalStatus - type: string - - targetFieldKey: national_identity_numbers - type: object - expression: | - [{ - "value": $.fetch_employee.ssn, - "type": "ssn" - }] - - targetFieldKey: bank_details - type: object - expression: $.fetch_bank_details.data - - targetFieldKey: title - expression: $.fetch_employee.jobTitle - type: string - - - stepId: typecast_employee_data - description: Apply unified schema types to combined employee data - stepFunction: - functionName: typecast - version: '2' - parameters: - dataSource: $.steps.map_employee_data.output.data - fields: - - targetFieldKey: id - type: string - - targetFieldKey: first_name - type: string - - targetFieldKey: last_name - type: string - - targetFieldKey: date_of_birth - type: datetime_string - - targetFieldKey: personal_email - type: string - - targetFieldKey: termination_date - type: datetime_string - - targetFieldKey: personal_phone_number - type: string - - targetFieldKey: start_date - type: datetime_string - - targetFieldKey: employee_number - type: string - - targetFieldKey: marital_status - type: string - - targetFieldKey: title - type: string - + condition: '{{present(inputs.fields)}}' result: - data: $.steps.typecast_employee_data.output.data + data: $.steps.fetch_employee.output.data - actionId: create_employee label: Create Employee categories: - hris - actionType: create + actionType: custom description: Creates a new employee record in BambooHR. - details: Creates a new employee with the provided personal and contact details. First name and last name are required. All other fields are optional and will only be sent if provided. - resources: https://documentation.bamboohr.com/reference/post-employee - entrypointUrl: /employees - entrypointHttpMethod: post - + details: Creates a new employee with the provided fields. firstName and lastName are required. All other fields are optional. Returns the Location header with the new employee URL. + resources: https://documentation.bamboohr.com/reference/add-employee-1 inputs: - - name: first_name - description: The employee's first name + - name: firstName + description: Employee first name type: string in: body required: true - - name: last_name - description: The employee's last name + - name: lastName + description: Employee last name type: string in: body required: true - - name: date_of_birth - description: The employee's date of birth in YYYY-MM-DD format - type: datetime_string + - name: jobTitle + description: Employee job title + type: string in: body required: false - - name: personal_email - description: The employee's personal email address + - name: department + description: Employee department name type: string in: body required: false - - name: personal_phone_number - description: The employee's personal mobile phone number + - name: workEmail + description: Employee work email address type: string in: body required: false - - name: start_date - description: The employee's hire date in YYYY-MM-DD format - type: datetime_string + - name: homeEmail + description: Employee personal email address + type: string in: body required: false - - name: title - description: The employee's job title + - name: mobilePhone + description: Employee mobile phone number type: string in: body required: false - - name: employee_number - description: The assigned employee number or ID + - name: hireDate + description: Employee hire date in YYYY-MM-DD format type: string in: body required: false - - name: marital_status - description: The employee's marital status + - name: dateOfBirth + description: Employee date of birth in YYYY-MM-DD format type: string in: body required: false - - name: home_location - description: The employee's home address details - type: object + - name: employeeNumber + description: Assigned employee number + type: string + in: body + required: false + - name: maritalStatus + description: Employee marital status + type: string + in: body + required: false + - name: address1 + description: First line of home address + type: string + in: body + required: false + - name: address2 + description: Second line of home address + type: string + in: body + required: false + - name: city + description: Home address city + type: string + in: body + required: false + - name: zipCode + description: Home address ZIP or postal code + type: string + in: body + required: false + - name: country + description: Home address country + type: string in: body required: false - properties: - - name: street_1 - description: First line of the home address - type: string - required: false - - name: street_2 - description: Second line of the home address - type: string - required: false - - name: city - description: City of the home address - type: string - required: false - - name: zip_code - description: ZIP or postal code of the home address - type: string - required: false - - name: country - description: Country of the home address - type: string - required: false - steps: - stepId: create_employee description: Create new employee record in BambooHR @@ -354,159 +164,169 @@ value: application/json in: headers - name: firstName - value: $.inputs.first_name + value: $.inputs.firstName in: body - name: lastName - value: $.inputs.last_name + value: $.inputs.lastName in: body - - name: dateOfBirth - value: $.inputs.date_of_birth + - name: jobTitle + value: $.inputs.jobTitle + in: body + condition: '{{present(inputs.jobTitle)}}' + - name: department + value: $.inputs.department in: body - condition: '{{present(inputs.date_of_birth)}}' + condition: '{{present(inputs.department)}}' + - name: workEmail + value: $.inputs.workEmail + in: body + condition: '{{present(inputs.workEmail)}}' - name: homeEmail - value: $.inputs.personal_email + value: $.inputs.homeEmail in: body - condition: '{{present(inputs.personal_email)}}' + condition: '{{present(inputs.homeEmail)}}' - name: mobilePhone - value: $.inputs.personal_phone_number + value: $.inputs.mobilePhone in: body - condition: '{{present(inputs.personal_phone_number)}}' + condition: '{{present(inputs.mobilePhone)}}' - name: hireDate - value: $.inputs.start_date + value: $.inputs.hireDate in: body - condition: '{{present(inputs.start_date)}}' - - name: jobTitle - value: $.inputs.title + condition: '{{present(inputs.hireDate)}}' + - name: dateOfBirth + value: $.inputs.dateOfBirth in: body - condition: '{{present(inputs.title)}}' + condition: '{{present(inputs.dateOfBirth)}}' - name: employeeNumber - value: $.inputs.employee_number + value: $.inputs.employeeNumber in: body - condition: '{{present(inputs.employee_number)}}' + condition: '{{present(inputs.employeeNumber)}}' - name: maritalStatus - value: $.inputs.marital_status + value: $.inputs.maritalStatus in: body - condition: '{{present(inputs.marital_status)}}' + condition: '{{present(inputs.maritalStatus)}}' - name: address1 - value: $.inputs.home_location.street_1 + value: $.inputs.address1 in: body - condition: '{{present(inputs.home_location.street_1)}}' + condition: '{{present(inputs.address1)}}' - name: address2 - value: $.inputs.home_location.street_2 + value: $.inputs.address2 in: body - condition: '{{present(inputs.home_location.street_2)}}' + condition: '{{present(inputs.address2)}}' - name: city - value: $.inputs.home_location.city + value: $.inputs.city in: body - condition: '{{present(inputs.home_location.city)}}' + condition: '{{present(inputs.city)}}' - name: zipCode - value: $.inputs.home_location.zip_code + value: $.inputs.zipCode in: body - condition: '{{present(inputs.home_location.zip_code)}}' + condition: '{{present(inputs.zipCode)}}' - name: country - value: $.inputs.home_location.country + value: $.inputs.country in: body - condition: '{{present(inputs.home_location.country)}}' - + condition: '{{present(inputs.country)}}' result: - message: Employee created successfully - data: - id: $.steps.create_employee.output.data.id + data: $.steps.create_employee.output.data - actionId: update_employee label: Update Employee categories: - hris - actionType: update + actionType: custom description: Updates an existing employee record in BambooHR. - details: Updates one or more fields on an existing employee. Only provided fields will be updated — all body fields are optional except the employee ID path parameter. BambooHR uses POST (not PATCH) for employee field updates. + details: Updates one or more fields on an existing employee. Only provided fields will be sent. BambooHR uses POST (not PATCH) for employee field updates. resources: https://documentation.bamboohr.com/reference/update-employee - entrypointUrl: /employees/:id - entrypointHttpMethod: post - inputs: - name: id - description: The unique identifier of the employee to update + description: The BambooHR employee ID type: string in: path required: true - - name: first_name - description: The employee's first name + - name: firstName + description: Employee first name type: string in: body required: false - - name: last_name - description: The employee's last name + - name: lastName + description: Employee last name type: string in: body required: false - - name: date_of_birth - description: The employee's date of birth in YYYY-MM-DD format - type: datetime_string + - name: jobTitle + description: Employee job title + type: string in: body required: false - - name: personal_email - description: The employee's personal email address + - name: department + description: Employee department name type: string in: body required: false - - name: personal_phone_number - description: The employee's personal mobile phone number + - name: workEmail + description: Employee work email address type: string in: body required: false - - name: start_date - description: The employee's hire date in YYYY-MM-DD format - type: datetime_string + - name: homeEmail + description: Employee personal email address + type: string in: body required: false - - name: termination_date - description: The employee's termination date in YYYY-MM-DD format - type: datetime_string + - name: mobilePhone + description: Employee mobile phone number + type: string in: body required: false - - name: title - description: The employee's job title + - name: hireDate + description: Employee hire date in YYYY-MM-DD format type: string in: body required: false - - name: employee_number - description: The assigned employee number or ID + - name: terminationDate + description: Employee termination date in YYYY-MM-DD format type: string in: body required: false - - name: marital_status - description: The employee's marital status + - name: dateOfBirth + description: Employee date of birth in YYYY-MM-DD format type: string in: body required: false - - name: home_location - description: The employee's home address details - type: object + - name: employeeNumber + description: Assigned employee number + type: string + in: body + required: false + - name: maritalStatus + description: Employee marital status + type: string + in: body + required: false + - name: address1 + description: First line of home address + type: string + in: body + required: false + - name: address2 + description: Second line of home address + type: string + in: body + required: false + - name: city + description: Home address city + type: string + in: body + required: false + - name: zipCode + description: Home address ZIP or postal code + type: string + in: body + required: false + - name: country + description: Home address country + type: string in: body required: false - properties: - - name: street_1 - description: First line of the home address - type: string - required: false - - name: street_2 - description: Second line of the home address - type: string - required: false - - name: city - description: City of the home address - type: string - required: false - - name: zip_code - description: ZIP or postal code of the home address - type: string - required: false - - name: country - description: Country of the home address - type: string - required: false - steps: - stepId: update_employee description: Update employee record fields in BambooHR @@ -523,94 +343,95 @@ value: application/json in: headers - name: firstName - value: $.inputs.first_name + value: $.inputs.firstName in: body - condition: '{{present(inputs.first_name)}}' + condition: '{{present(inputs.firstName)}}' - name: lastName - value: $.inputs.last_name + value: $.inputs.lastName in: body - condition: '{{present(inputs.last_name)}}' - - name: dateOfBirth - value: $.inputs.date_of_birth + condition: '{{present(inputs.lastName)}}' + - name: jobTitle + value: $.inputs.jobTitle in: body - condition: '{{present(inputs.date_of_birth)}}' + condition: '{{present(inputs.jobTitle)}}' + - name: department + value: $.inputs.department + in: body + condition: '{{present(inputs.department)}}' + - name: workEmail + value: $.inputs.workEmail + in: body + condition: '{{present(inputs.workEmail)}}' - name: homeEmail - value: $.inputs.personal_email + value: $.inputs.homeEmail in: body - condition: '{{present(inputs.personal_email)}}' + condition: '{{present(inputs.homeEmail)}}' - name: mobilePhone - value: $.inputs.personal_phone_number + value: $.inputs.mobilePhone in: body - condition: '{{present(inputs.personal_phone_number)}}' + condition: '{{present(inputs.mobilePhone)}}' - name: hireDate - value: $.inputs.start_date + value: $.inputs.hireDate in: body - condition: '{{present(inputs.start_date)}}' + condition: '{{present(inputs.hireDate)}}' - name: terminationDate - value: $.inputs.termination_date + value: $.inputs.terminationDate in: body - condition: '{{present(inputs.termination_date)}}' - - name: jobTitle - value: $.inputs.title + condition: '{{present(inputs.terminationDate)}}' + - name: dateOfBirth + value: $.inputs.dateOfBirth in: body - condition: '{{present(inputs.title)}}' + condition: '{{present(inputs.dateOfBirth)}}' - name: employeeNumber - value: $.inputs.employee_number + value: $.inputs.employeeNumber in: body - condition: '{{present(inputs.employee_number)}}' + condition: '{{present(inputs.employeeNumber)}}' - name: maritalStatus - value: $.inputs.marital_status + value: $.inputs.maritalStatus in: body - condition: '{{present(inputs.marital_status)}}' + condition: '{{present(inputs.maritalStatus)}}' - name: address1 - value: $.inputs.home_location.street_1 + value: $.inputs.address1 in: body - condition: '{{present(inputs.home_location.street_1)}}' + condition: '{{present(inputs.address1)}}' - name: address2 - value: $.inputs.home_location.street_2 + value: $.inputs.address2 in: body - condition: '{{present(inputs.home_location.street_2)}}' + condition: '{{present(inputs.address2)}}' - name: city - value: $.inputs.home_location.city + value: $.inputs.city in: body - condition: '{{present(inputs.home_location.city)}}' + condition: '{{present(inputs.city)}}' - name: zipCode - value: $.inputs.home_location.zip_code + value: $.inputs.zipCode in: body - condition: '{{present(inputs.home_location.zip_code)}}' + condition: '{{present(inputs.zipCode)}}' - name: country - value: $.inputs.home_location.country + value: $.inputs.country in: body - condition: '{{present(inputs.home_location.country)}}' - + condition: '{{present(inputs.country)}}' result: - message: Employee updated successfully - data: - id: $.inputs.id + data: $.steps.update_employee.output.data - actionId: terminate_employee label: Terminate Employee categories: - hris - actionType: delete + actionType: custom description: Terminates an employee in BambooHR by setting their termination date. - details: BambooHR does not support hard deletion of employee records. This action terminates an employee by setting their termination date. The employee record remains accessible in the system after termination. + details: BambooHR does not support hard deletion of employee records. This action terminates an employee by setting their terminationDate field. The employee record remains accessible in the system after termination. resources: https://documentation.bamboohr.com/reference/update-employee - entrypointUrl: /employees/:id - entrypointHttpMethod: post - inputs: - name: id - description: The unique identifier of the employee to terminate + description: The BambooHR employee ID type: string in: path required: true - - name: termination_date + - name: terminationDate description: The termination date in YYYY-MM-DD format - type: datetime_string + type: string in: body required: true - steps: - stepId: terminate_employee description: Set termination date on employee record in BambooHR @@ -627,10 +448,7 @@ value: application/json in: headers - name: terminationDate - value: $.inputs.termination_date + value: $.inputs.terminationDate in: body - result: - message: Employee terminated successfully - data: - id: $.inputs.id + data: $.steps.terminate_employee.output.data diff --git a/src/configs/breathehr/breathehr_v1-0-1.s1.yaml b/src/configs/breathehr/breathehr_v1-0-1.s1.yaml deleted file mode 100644 index 4559d62..0000000 --- a/src/configs/breathehr/breathehr_v1-0-1.s1.yaml +++ /dev/null @@ -1,2412 +0,0 @@ -StackOne: 1.0.0 -info: - title: BreatheHR - key: breathehr - version: 1.0.1 - assets: - icon: https://stackone-logos.com/api/breathe/filled/png - description: BreatheHR is a modern HR platform that helps companies streamline their hiring process. -baseUrl: https://api.breathehr.com/v1 -authentication: - - custom: - type: custom - label: Public API - support: - link: https://hub.stackone.com/connection-guides/hris/breathehr - description: The steps in this guide require Admin privileges within BreatheHR. - authorization: - type: none - configFields: - - key: apiKey - label: API Key - type: password - required: true - secret: true - placeholder: 7cef933f17737108009021191848635eb898b1b99cg2ffee227b577024ddfb2b - description: You can generate an API Key in your BreatheHR account **Configure > Settings > Integrations > API Setup**. - environments: - - key: production - name: Production - testActions: - - action: list_employees - required: true -actions: - - actionId: list_employees - categories: - - hris - actionType: list - schema: employees - label: List Employees - description: Get a list of employees - details: "Returns a paginated list of all employees in the BreatheHR system. Supports filtering by department_id, location_id, division_id, employment_status, and pagination with page and per_page. Returns employee data including ID, first_name, last_name, email, employment_status, department, location, and other employee properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - - name: filter - description: Type of employees to return - type: string - in: query - required: false - - name: rotacloud - description: Return leave requests where rotacloud is not excluded from integration - type: boolean - in: query - required: false - steps: - - stepId: get_all_employees - description: Get employees request - stepFunction: - functionName: request - parameters: - url: /employees - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - - name: filter - value: '{{$.inputs.filter}}' - in: query - condition: '{{present($.inputs.filter)}}' - - name: rotacloud - value: '{{$.inputs.rotacloud}}' - in: query - condition: '{{present($.inputs.rotacloud)}}' - response: - collection: true - result: - data: $.steps.get_all_employees.output.data - - actionId: get_employee - categories: - - hris - actionType: get - schema: employees - label: Get Employee - description: Get a specific employee by ID - details: "Returns detailed information for a specific employee by their ID. Includes employee ID, first_name, last_name, email, employment_status, department, location, division, job_title, start_date, and all other employee properties." - inputs: - - name: id - description: The ID of the employee to retrieve. - type: string - in: path - required: true - steps: - - stepId: get_an_employee - description: Get employee request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id} - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.get_an_employee.output.data - - actionId: list_employee_absences - categories: - - hris - actionType: get - label: Get Employee Absences - description: Get absences for a specific employee by ID - details: "Returns a paginated list of absences for a specific employee. Requires employee ID. Supports optional filtering to exclude cancelled absences and pagination with page and per_page parameters. Returns absence data including dates, reasons, and other absence properties." - inputs: - - name: id - description: The ID of the employee whose absences to retrieve. - type: string - in: path - required: true - - name: exclude_cancelled_absences - description: Don't return cancelled leave requests - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_employee_absences - description: Get employee absences request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/absences - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - - name: exclude_cancelled_absences - value: '{{$.inputs.exclude_cancelled_absences}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_absences)}}' - response: - collection: true - result: - data: $.steps.get_employee_absences.output.data - - actionId: list_employee_benefits - categories: - - hris - actionType: get - label: Get Employee Benefits - description: Get benefits for a specific employee by ID - details: "Returns a paginated list of benefits for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns benefit data including benefit types, coverage details, and other benefit properties." - inputs: - - name: id - description: The ID of the employee whose benefits to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_employee_benefits - description: Get employee benefits request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/benefits - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_employee_benefits.output.data - - actionId: list_employee_bonuses - categories: - - hris - actionType: get - label: Get Employee Bonuses - description: Get bonuses for a specific employee by ID - details: "Returns a paginated list of bonuses for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns bonus data including amounts, dates, reasons, and other bonus properties." - inputs: - - name: id - description: The ID of the employee whose bonuses to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_employee_bonuses - description: Get employee bonuses request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/bonuses - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_employee_bonuses.output.data - - actionId: list_employee_change_requests - categories: - - hris - actionType: get - label: Get Employee Change Requests - description: Get change requests for a specific employee by ID - details: "Returns a paginated list of change requests for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns change request data including request types, status, dates, and other change request properties." - inputs: - - name: id - description: The ID of the employee whose change requests to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_employee_change_requests - description: Get employee change requests request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/change_requests - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_employee_change_requests.output.data - - actionId: list_employee_holiday_years - categories: - - hris - actionType: get - label: Get Employee Holiday Years - description: Get holiday years for a specific employee by ID - details: "Returns a list of holiday years for a specific employee. Requires employee ID. Supports optional filtering by date (for_date) to return holiday years that include a specific date. Returns holiday year data including year, allowance, taken, remaining, and other holiday year properties." - inputs: - - name: id - description: ID of employee to return holiday years for. - type: string - in: path - required: true - - name: for_date - description: Return employee_holiday_years that include this date. - type: string - in: query - required: false - steps: - - stepId: get_employee_holiday_years - description: Get employee holiday years request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/employee_holiday_years - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: for_date - value: '{{$.inputs.for_date}}' - in: query - condition: '{{present($.inputs.for_date)}}' - response: - collection: true - result: - data: $.steps.get_employee_holiday_years.output.data - - actionId: list_employee_leave_requests - categories: - - hris - actionType: get - label: Get Employee Leave Requests - description: Get leave requests for a specific employee by ID - details: "Returns a list of leave requests for a specific employee. Requires employee ID. Supports optional filtering to exclude cancelled requests. Returns leave request data including dates, type, status, and other leave request properties." - inputs: - - name: id - description: ID of employee to return leave requests for. - type: string - in: path - required: true - - name: exclude_cancelled_requests - description: Don't return cancelled leave requests. - type: boolean - in: query - required: false - steps: - - stepId: get_employee_leave_requests - description: Get employee leave requests request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/leave_requests - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: exclude_cancelled_requests - value: '{{$.inputs.exclude_cancelled_requests}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_requests)}}' - response: - collection: true - result: - data: $.steps.get_employee_leave_requests.output.data - - actionId: list_employee_salaries - categories: - - hris - actionType: get - label: Get Employee Salaries - description: Get salaries for a specific employee by ID - details: "Returns a paginated list of salaries for a specific employee. Requires employee ID. Supports pagination with page and per_page parameters. Returns salary data including amounts, effective dates, and other salary properties." - inputs: - - name: id - description: The ID of the employee whose salaries to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_employee_salaries - description: Get employee salaries request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/salaries - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_employee_salaries.output.data - - actionId: list_employee_sicknesses - categories: - - hris - actionType: get - label: Get Employee Sicknesses - description: Get sicknesses for a specific employee by ID - details: "Returns a list of sickness records for a specific employee. Requires employee ID. Returns sickness data including dates, type, status, reason, and other sickness properties." - inputs: - - name: id - description: ID of employee to return sicknesses for. - type: string - in: path - required: true - steps: - - stepId: get_employee_sicknesses - description: Get employee sicknesses request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/sicknesses - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_employee_sicknesses.output.data - - actionId: create_employee_leave_request - categories: - - hris - actionType: create - label: Create Employee Leave Request - description: Create a leave request for a specific employee - details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." - inputs: - - name: id - description: ID of employee to create leave request for. - type: number - in: path - required: true - - name: exclude_cancelled_requests - description: Don't return cancelled leave requests. - type: boolean - in: body - required: false - - name: leave_request[start_date] - description: Start date of the leave request. - type: string - in: body - required: true - - name: leave_request[end_date] - description: End date of the leave request. - type: string - in: body - required: true - - name: leave_request[half_start] - description: Whether the start date is a half day. - type: boolean - in: body - required: true - - name: leave_request[half_start_am_pm] - description: Whether the half start day is AM or PM. - type: string - in: body - required: false - - name: leave_request[half_end] - description: Whether the end date is a half day. - type: boolean - in: body - required: false - - name: leave_request[half_end_am_pm] - description: Whether the half end day is AM or PM. - type: string - in: body - required: false - - name: leave_request[type] - description: Type of leave request. - type: string - in: body - required: false - - name: leave_request[notes] - description: Notes for the leave request. - type: string - in: body - required: false - steps: - - stepId: create_leave_request - description: Create employee leave request - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/leave_requests - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: exclude_cancelled_requests - value: '{{$.inputs.exclude_cancelled_requests}}' - in: body - condition: '{{present($.inputs.exclude_cancelled_requests)}}' - - name: leave_request[start_date] - value: "{{$.inputs['leave_request[start_date]']}}" - in: body - - name: leave_request[end_date] - value: "{{$.inputs['leave_request[end_date]']}}" - in: body - - name: leave_request[half_start] - value: "{{$.inputs['leave_request[half_start]']}}" - in: body - - name: leave_request[half_start_am_pm] - value: "{{$.inputs['leave_request[half_start_am_pm]']}}" - in: body - condition: "{{present($.inputs['leave_request[half_start_am_pm]'])}}" - - name: leave_request[half_end] - value: "{{$.inputs['leave_request[half_end]']}}" - in: body - condition: "{{present($.inputs['leave_request[half_end]'])}}" - - name: leave_request[half_end_am_pm] - value: "{{$.inputs['leave_request[half_end_am_pm]']}}" - in: body - condition: "{{present($.inputs['leave_request[half_end_am_pm]'])}}" - - name: leave_request[type] - value: "{{$.inputs['leave_request[type]']}}" - in: body - condition: "{{present($.inputs['leave_request[type]'])}}" - - name: leave_request[notes] - value: "{{$.inputs['leave_request[notes]']}}" - in: body - condition: "{{present($.inputs['leave_request[notes]'])}}" - response: - collection: false - result: - data: $.steps.create_leave_request.output.data - - actionId: create_employee_sickness - categories: - - hris - actionType: create - label: Create Employee Sickness - description: Create a sickness record for a specific employee - details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." - inputs: - - name: id - description: Employee id. - type: number - in: path - required: true - - name: sickness[company_sicknesstype_id] - description: Company sickness type ID. - type: number - in: body - required: true - - name: sickness[start_date] - description: Start date of the sickness. - type: string - in: body - required: true - - name: sickness[half_start] - description: Whether the start date is a half day. - type: boolean - in: body - required: false - - name: sickness[half_start_am_pm] - description: Whether the half start day is AM or PM. - type: string - in: body - required: false - - name: sickness[half_end] - description: Whether the end date is a half day. - type: boolean - in: body - required: false - - name: sickness[half_end_am_pm] - description: Whether the half end day is AM or PM. - type: string - in: body - required: false - - name: sickness[half_day] - description: Whether it is a half day sickness. - type: boolean - in: body - required: false - - name: sickness[status] - description: Status of the sickness. - type: string - in: body - required: false - - name: sickness[reason] - description: Reason for the sickness. - type: string - in: body - required: false - - name: sickness[end_date] - description: End date of the sickness. - type: string - in: body - required: false - - name: sickness[review_notes] - description: Review notes for the sickness. - type: string - in: body - required: false - steps: - - stepId: create_sickness - description: Create employee sickness - stepFunction: - functionName: request - parameters: - url: /employees/${inputs.id}/sicknesses - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: sickness[company_sicknesstype_id] - value: "{{$.inputs['sickness[company_sicknesstype_id]']}}" - in: body - - name: sickness[start_date] - value: "{{$.inputs['sickness[start_date]']}}" - in: body - - name: sickness[half_start] - value: "{{$.inputs['sickness[half_start]']}}" - in: body - condition: "{{present($.inputs['sickness[half_start]'])}}" - - name: sickness[half_start_am_pm] - value: "{{$.inputs['sickness[half_start_am_pm]']}}" - in: body - condition: "{{present($.inputs['sickness[half_start_am_pm]'])}}" - - name: sickness[half_end] - value: "{{$.inputs['sickness[half_end]']}}" - in: body - condition: "{{present($.inputs['sickness[half_end]'])}}" - - name: sickness[half_end_am_pm] - value: "{{$.inputs['sickness[half_end_am_pm]']}}" - in: body - condition: "{{present($.inputs['sickness[half_end_am_pm]'])}}" - - name: sickness[half_day] - value: "{{$.inputs['sickness[half_day]']}}" - in: body - condition: "{{present($.inputs['sickness[half_day]'])}}" - - name: sickness[status] - value: "{{$.inputs['sickness[status]']}}" - in: body - condition: "{{present($.inputs['sickness[status]'])}}" - - name: sickness[reason] - value: "{{$.inputs['sickness[reason]']}}" - in: body - condition: "{{present($.inputs['sickness[reason]'])}}" - - name: sickness[end_date] - value: "{{$.inputs['sickness[end_date]']}}" - in: body - condition: "{{present($.inputs['sickness[end_date]'])}}" - - name: sickness[review_notes] - value: "{{$.inputs['sickness[review_notes]']}}" - in: body - condition: "{{present($.inputs['sickness[review_notes]'])}}" - response: - collection: false - result: - data: $.steps.create_sickness.output.data - # Blind Mapped as the provider gives 500 while creating with right payload (need to recheck) - - actionId: create_employee - published: false - categories: - - hris - actionType: create - schema: employees - label: Create Employee - description: Create a new employee - details: "Creates a new employee in BreatheHR. Requires first_name, last_name, and email. Supports optional fields including employment_status, department_id, location_id, division_id, job_title, start_date, and other employee properties. Returns the created employee with ID and all properties." - inputs: - - name: employee[first_name] - description: First name. - type: string - in: body - required: true - - name: employee[last_name] - description: Last name. - type: string - in: body - required: true - - name: employee[email] - description: Work email. - type: string - in: body - required: true - - name: employee[company_join_date] - description: Company join date. - type: string - in: body - required: true - - name: employee[middle_name] - description: Middle name. - type: string - in: body - required: false - - name: employee[gender] - description: Gender id. - type: string - in: body - required: false - - name: employee[dob] - description: Date of birth. - type: string - in: body - required: false - - name: employee[marital_status] - description: Marital status. - type: string - in: body - required: false - - name: employee[nationality] - description: Nationality. - type: string - in: body - required: false - - name: employee[national_insurance_no] - description: National Insurance Number. - type: string - in: body - required: false - - name: employee[ethnicity] - description: Ethnicity. - type: string - in: body - required: false - - name: employee[ethnicity_id] - description: Ethnicity id. - type: string - in: body - required: false - - name: employee[ddi] - description: Direct dial-in number. - type: string - in: body - required: false - - name: employee[work_ext] - description: Work extension number. - type: string - in: body - required: false - - name: employee[work_mobile] - description: Work mobile number. - type: string - in: body - required: false - - name: employee[personal_mobile] - description: Personal mobile number. - type: string - in: body - required: false - - name: employee[home_telephone] - description: Home telephone number. - type: string - in: body - required: false - - name: employee[status] - description: Employee status. - type: string - in: body - required: false - - name: employee[job_title] - description: Job title. - type: string - in: body - required: false - - name: employee[full_or_part_time] - description: Fulltime or parttime employee. - type: string - in: body - required: false - - name: employee[job_start_date] - description: Job start date. - type: string - in: body - required: false - - name: employee[employee_ref] - description: Employee reference number. - type: string - in: body - required: false - - name: employee[person_type] - description: Person type. - type: string - in: body - required: false - - name: employee[department] - description: Department id. - type: number - in: body - required: false - - name: employee[division] - description: Division id. - type: number - in: body - required: false - - name: employee[location] - description: Location id. - type: number - in: body - required: false - - name: employee[working_pattern] - description: Working pattern id. - type: number - in: body - required: false - - name: employee[working_pattern_id] - description: Working pattern id. - type: number - in: body - required: false - - name: employee[holiday_allowance] - description: Holiday Allowance id. - type: number - in: body - required: false - - name: employee[holiday_allowance_id] - description: Holiday Allowance id. - type: number - in: body - required: false - - name: employee[receives_statutory_holidays] - description: Employee receives statutory holidays for their country. - type: boolean - in: body - required: false - - name: employee[statutory_holiday_country] - description: Statutory holiday country name. - type: string - in: body - required: false - - name: employee[statutory_holiday_country_id] - description: Statutory holiday country id. - type: number - in: body - required: false - - name: employee[ignore_working_pattern] - description: Ignore the working pattern and allow manual entry of time lost when requesting leave. - type: boolean - in: body - required: false - - name: employee[hr] - description: Employee is an HR manager. - type: boolean - in: body - required: false - - name: employee[salary][amount] - description: Salary amount. - type: number - in: body - required: false - - name: employee[salary][basis] - description: Salary basis. - type: string - in: body - required: false - - name: employee[salary][start_date] - description: Salary start date. - type: string - in: body - required: false - - name: employee[salary][payment_schedule] - description: Salary payment schedule. - type: string - in: body - required: false - steps: - - stepId: create_new_employee - description: Create new employee - stepFunction: - functionName: request - parameters: - url: /employees - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: employee[first_name] - value: "{{$.inputs['employee[first_name]']}}" - in: body - - name: employee[last_name] - value: "{{$.inputs['employee[last_name]']}}" - in: body - - name: employee[email] - value: "{{$.inputs['employee[email]']}}" - in: body - - name: employee[company_join_date] - value: "{{$.inputs['employee[company_join_date]']}}" - in: body - - name: employee[middle_name] - value: "{{$.inputs['employee[middle_name]']}}" - in: body - condition: "{{present($.inputs['employee[middle_name]'])}}" - - name: employee[gender] - value: "{{$.inputs['employee[gender]']}}" - in: body - condition: "{{present($.inputs['employee[gender]'])}}" - - name: employee[dob] - value: "{{$.inputs['employee[dob]']}}" - in: body - condition: "{{present($.inputs['employee[dob]'])}}" - - name: employee[marital_status] - value: "{{$.inputs['employee[marital_status]']}}" - in: body - condition: "{{present($.inputs['employee[marital_status]'])}}" - - name: employee[nationality] - value: "{{$.inputs['employee[nationality]']}}" - in: body - condition: "{{present($.inputs['employee[nationality]'])}}" - - name: employee[national_insurance_no] - value: "{{$.inputs['employee[national_insurance_no]']}}" - in: body - condition: "{{present($.inputs['employee[national_insurance_no]'])}}" - - name: employee[ethnicity] - value: "{{$.inputs['employee[ethnicity]']}}" - in: body - condition: "{{present($.inputs['employee[ethnicity]'])}}" - - name: employee[ethnicity_id] - value: "{{$.inputs['employee[ethnicity_id]']}}" - in: body - condition: "{{present($.inputs['employee[ethnicity_id]'])}}" - - name: employee[ddi] - value: "{{$.inputs['employee[ddi]']}}" - in: body - condition: "{{present($.inputs['employee[ddi]'])}}" - - name: employee[work_ext] - value: "{{$.inputs['employee[work_ext]']}}" - in: body - condition: "{{present($.inputs['employee[work_ext]'])}}" - - name: employee[work_mobile] - value: "{{$.inputs['employee[work_mobile]']}}" - in: body - condition: "{{present($.inputs['employee[work_mobile]'])}}" - - name: employee[personal_mobile] - value: "{{$.inputs['employee[personal_mobile]']}}" - in: body - condition: "{{present($.inputs['employee[personal_mobile]'])}}" - - name: employee[home_telephone] - value: "{{$.inputs['employee[home_telephone]']}}" - in: body - condition: "{{present($.inputs['employee[home_telephone]'])}}" - - name: employee[status] - value: "{{$.inputs['employee[status]']}}" - in: body - condition: "{{present($.inputs['employee[status]'])}}" - - name: employee[job_title] - value: "{{$.inputs['employee[job_title]']}}" - in: body - condition: "{{present($.inputs['employee[job_title]'])}}" - - name: employee[full_or_part_time] - value: "{{$.inputs['employee[full_or_part_time]']}}" - in: body - condition: "{{present($.inputs['employee[full_or_part_time]'])}}" - - name: employee[job_start_date] - value: "{{$.inputs['employee[job_start_date]']}}" - in: body - condition: "{{present($.inputs['employee[job_start_date]'])}}" - - name: employee[employee_ref] - value: "{{$.inputs['employee[employee_ref]']}}" - in: body - condition: "{{present($.inputs['employee[employee_ref]'])}}" - - name: employee[person_type] - value: "{{$.inputs['employee[person_type]']}}" - in: body - condition: "{{present($.inputs['employee[person_type]'])}}" - - name: employee[department] - value: "{{$.inputs['employee[department]']}}" - in: body - condition: "{{present($.inputs['employee[department]'])}}" - - name: employee[division] - value: "{{$.inputs['employee[division]']}}" - in: body - condition: "{{present($.inputs['employee[division]'])}}" - - name: employee[location] - value: "{{$.inputs['employee[location]']}}" - in: body - condition: "{{present($.inputs['employee[location]'])}}" - - name: employee[working_pattern] - value: "{{$.inputs['employee[working_pattern]']}}" - in: body - condition: "{{present($.inputs['employee[working_pattern]'])}}" - - name: employee[working_pattern_id] - value: "{{$.inputs['employee[working_pattern_id]']}}" - in: body - condition: "{{present($.inputs['employee[working_pattern_id]'])}}" - - name: employee[holiday_allowance] - value: "{{$.inputs['employee[holiday_allowance]']}}" - in: body - condition: "{{present($.inputs['employee[holiday_allowance]'])}}" - - name: employee[holiday_allowance_id] - value: "{{$.inputs['employee[holiday_allowance_id]']}}" - in: body - condition: "{{present($.inputs['employee[holiday_allowance_id]'])}}" - - name: employee[receives_statutory_holidays] - value: "{{$.inputs['employee[receives_statutory_holidays]']}}" - in: body - condition: "{{present($.inputs['employee[receives_statutory_holidays]'])}}" - - name: employee[statutory_holiday_country] - value: "{{$.inputs['employee[statutory_holiday_country]']}}" - in: body - condition: "{{present($.inputs['employee[statutory_holiday_country]'])}}" - - name: employee[statutory_holiday_country_id] - value: "{{$.inputs['employee[statutory_holiday_country_id]']}}" - in: body - condition: "{{present($.inputs['employee[statutory_holiday_country_id]'])}}" - - name: employee[ignore_working_pattern] - value: "{{$.inputs['employee[ignore_working_pattern]']}}" - in: body - condition: "{{present($.inputs['employee[ignore_working_pattern]'])}}" - - name: employee[hr] - value: "{{$.inputs['employee[hr]']}}" - in: body - condition: "{{present($.inputs['employee[hr]'])}}" - - name: employee[salary][amount] - value: "{{$.inputs['employee[salary][amount]']}}" - in: body - condition: "{{present($.inputs['employee[salary][amount]'])}}" - - name: employee[salary][basis] - value: "{{$.inputs['employee[salary][basis]']}}" - in: body - condition: "{{present($.inputs['employee[salary][basis]'])}}" - - name: employee[salary][start_date] - value: "{{$.inputs['employee[salary][start_date]']}}" - in: body - condition: "{{present($.inputs['employee[salary][start_date]'])}}" - - name: employee[salary][payment_schedule] - value: "{{$.inputs['employee[salary][payment_schedule]']}}" - in: body - condition: "{{present($.inputs['employee[salary][payment_schedule]'])}}" - response: - collection: false - result: - data: $.steps.create_new_employee.output.data - - actionId: list_holiday_allowances - categories: - - hris - actionType: list - label: List Holiday Allowances - description: Get a list of holiday allowances - details: "Returns a paginated list of all holiday allowances in the BreatheHR system. Supports pagination with page and per_page. Returns holiday allowance data including ID, employee_id, allowance_type, days, and other allowance properties." - steps: - - stepId: get_all_holiday_allowances - description: Get holiday allowances request - stepFunction: - functionName: request - parameters: - url: /holiday_allowances - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_all_holiday_allowances.output.data - - actionId: list_statutory_holiday_countries - categories: - - hris - actionType: list - label: List Statutory Holiday Countries - description: Get a list of statutory holiday countries - details: "Returns a list of all statutory holiday countries configured in BreatheHR. Returns country data including country code, name, and other country properties." - steps: - - stepId: get_statutory_holiday_countries - description: Get statutory holiday countries request - stepFunction: - functionName: request - parameters: - url: /statutory_holiday_countries - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_statutory_holiday_countries.output.data - - actionId: list_leave_requests - categories: - - hris - actionType: list - label: List Leave Requests - description: Get a list of leave requests - details: "Returns a paginated list of all leave requests in the BreatheHR system. Supports filtering by employee_id, department_id, status, start_date, end_date, and pagination with page and per_page. Returns leave request data including ID, employee_id, type, start_date, end_date, status, and other leave request properties." - inputs: - - name: start_date - description: Return leave requests starting on or after this date. - type: string - in: query - required: false - - name: end_date - description: Return leave requests starting on or before this date. - type: string - in: query - required: false - - name: exclude_cancelled_requests - description: Don't return cancelled leave requests. - type: boolean - in: query - required: false - - name: employee_id - description: Return leave requests for this employee only. - type: number - in: query - required: false - - name: department_id - description: Return leave requests for this department only. - type: number - in: query - required: false - - name: rotacloud - description: Return leave requests where rotacloud is not excluded from integration. - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_leave_requests - description: Get leave requests request - stepFunction: - functionName: request - parameters: - url: /leave_requests - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: start_date - value: '{{$.inputs.start_date}}' - in: query - condition: '{{present($.inputs.start_date)}}' - - name: end_date - value: '{{$.inputs.end_date}}' - in: query - condition: '{{present($.inputs.end_date)}}' - - name: exclude_cancelled_requests - value: '{{$.inputs.exclude_cancelled_requests}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_requests)}}' - - name: employee_id - value: '{{$.inputs.employee_id}}' - in: query - condition: '{{present($.inputs.employee_id)}}' - - name: department_id - value: '{{$.inputs.department_id}}' - in: query - condition: '{{present($.inputs.department_id)}}' - - name: rotacloud - value: '{{$.inputs.rotacloud}}' - in: query - condition: '{{present($.inputs.rotacloud)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_leave_requests.output.data - - actionId: get_leave_request - categories: - - hris - actionType: get - label: Get Leave Request - description: Get a specific leave request by ID - details: "Returns detailed information for a specific leave request by its ID. Includes leave request ID, employee_id, type, start_date, end_date, status, and all other leave request properties." - inputs: - - name: id - description: The ID of the leave request to retrieve. - type: string - in: path - required: true - steps: - - stepId: get_a_leave_request - description: Get leave request request - stepFunction: - functionName: request - parameters: - url: /leave_requests/${inputs.id} - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.get_a_leave_request.output.data - - actionId: get_leave_request_cancelling - categories: - - hris - actionType: get - label: Get Leave Request Cancelling - description: Find the request this leave request is cancelling - details: "Returns detailed information for a specific leave request by its ID. Includes leave request ID, employee_id, type, start_date, end_date, status, and all other leave request properties." - inputs: - - name: id - description: The ID of the leave request. - type: string - in: path - required: true - steps: - - stepId: get_leave_request_cancelling_step - description: Get leave request cancelling request - stepFunction: - functionName: request - parameters: - url: /leave_requests/${inputs.id}/cancelling - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.get_leave_request_cancelling_step.output.data - - actionId: list_other_leave_reasons - categories: - - hris - actionType: list - label: List Other Leave Reasons - description: Get a list of other leave reasons - details: "Returns a list of all other leave reasons configured in BreatheHR. Returns leave reason data including ID, name, and other leave reason properties." - steps: - - stepId: get_other_leave_reasons - description: Get other leave reasons request - stepFunction: - functionName: request - parameters: - url: /leave_requests/other_leave_reasons - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_other_leave_reasons.output.data - - actionId: list_other_leave_reasons_main - categories: - - hris - actionType: list - label: List Other Leave Reasons - description: Get a list of other leave reasons - details: "Returns a list of all other leave reasons configured in BreatheHR. Returns leave reason data including ID, name, and other leave reason properties." - steps: - - stepId: get_all_other_leave_reasons - description: Get other leave reasons request - stepFunction: - functionName: request - parameters: - url: /other_leave_reasons - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_all_other_leave_reasons.output.data - - actionId: approve_leave_request - categories: - - hris - actionType: update - label: Approve Leave Request - description: Approve a specific leave request by ID - details: "Approves a leave request in BreatheHR. Requires leave request ID. Updates the leave request status to approved. Returns the updated leave request with approval status." - inputs: - - name: id - description: The ID of the leave request to approve. - type: string - in: path - required: true - steps: - - stepId: approve_leave_request - description: Approve leave request - stepFunction: - functionName: request - parameters: - url: /leave_requests/${inputs.id}/approve - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.approve_leave_request.output.data - - actionId: reject_leave_request - categories: - - hris - actionType: update - label: Reject Leave Request - description: Reject a specific leave request by ID - details: "Rejects a leave request in BreatheHR. Requires leave request ID and rejection_reason. Updates the leave request status to rejected. Returns the updated leave request with rejection status and reason." - inputs: - - name: id - description: The ID of the leave request to reject. - type: string - in: path - required: true - - name: leave_request[rejection_reason] - description: Rejection reason. - type: string - in: body - required: true - steps: - - stepId: reject_leave_request - description: Reject leave request - stepFunction: - functionName: request - parameters: - url: /leave_requests/${inputs.id}/reject - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: leave_request[rejection_reason] - value: "{{$.inputs['leave_request[rejection_reason]']}}" - in: body - response: - collection: false - result: - data: $.steps.reject_leave_request.output.data - - actionId: list_locations - categories: - - hris - actionType: list - label: List Locations - description: Get a list of locations - details: "Returns a paginated list of all locations in the BreatheHR system. Supports pagination with page and per_page. Returns location data including ID, name, address, and other location properties." - steps: - - stepId: get_all_locations - description: Get locations request - stepFunction: - functionName: request - parameters: - url: /locations - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_all_locations.output.data - - actionId: list_salaries - categories: - - hris - actionType: list - label: List Salaries - description: Get a list of salaries - details: "Returns a paginated list of all salaries in the BreatheHR system. Supports pagination with page and per_page. Returns salary data including ID, employee_id, amount, currency, frequency, and other salary properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_salaries - description: Get salaries request - stepFunction: - functionName: request - parameters: - url: /salaries - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_salaries.output.data - - actionId: list_sicknesses - categories: - - hris - actionType: list - label: List Sicknesses - description: Get a list of sicknesses - details: "Returns a paginated list of all sickness records in the BreatheHR system. Supports filtering by employee_id, department_id, start_date, end_date, and pagination with page and per_page. Returns sickness data including ID, employee_id, start_date, end_date, status, and other sickness properties." - inputs: - - name: start_date - description: Return sicknesses starting on or after this date. - type: string - in: query - required: false - - name: end_date - description: Return sicknesses starting on or before this date. - type: string - in: query - required: false - - name: employee_id - description: Return sicknesses for this employee only. - type: number - in: query - required: false - - name: department_id - description: Return sicknesses for this department only. - type: number - in: query - required: false - - name: rotacloud - description: Return sicknesses for employees without exclude from rotacloud. - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_sicknesses - description: Get sicknesses request - stepFunction: - functionName: request - parameters: - url: /sicknesses - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: start_date - value: '{{$.inputs.start_date}}' - in: query - condition: '{{present($.inputs.start_date)}}' - - name: end_date - value: '{{$.inputs.end_date}}' - in: query - condition: '{{present($.inputs.end_date)}}' - - name: employee_id - value: '{{$.inputs.employee_id}}' - in: query - condition: '{{present($.inputs.employee_id)}}' - - name: department_id - value: '{{$.inputs.department_id}}' - in: query - condition: '{{present($.inputs.department_id)}}' - - name: rotacloud - value: '{{$.inputs.rotacloud}}' - in: query - condition: '{{present($.inputs.rotacloud)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_sicknesses.output.data - - actionId: update_sickness - categories: - - hris - actionType: update - label: Update Sickness - description: Update a specific sickness record by ID - details: "Updates an existing sickness record. Requires sickness ID. Supports updating start_date, end_date, status, and other sickness properties. Returns the updated sickness record with all properties." - inputs: - - name: id - description: Sickness ID. - type: number - in: path - required: true - - name: sickness[company_sicknesstype_id] - description: Company sickness type ID. - type: number - in: body - required: false - - name: sickness[start_date] - description: Start date of the sickness. - type: string - in: body - required: false - - name: sickness[half_start] - description: Whether the start date is a half day. - type: boolean - in: body - required: false - - name: sickness[half_start_am_pm] - description: Whether the half start day is AM or PM. - type: string - in: body - required: false - - name: sickness[half_end] - description: Whether the end date is a half day. - type: boolean - in: body - required: false - - name: sickness[half_end_am_pm] - description: Whether the half end day is AM or PM. - type: string - in: body - required: false - - name: sickness[half_day] - description: Whether it is a half day sickness. - type: boolean - in: body - required: false - - name: sickness[status] - description: Status of the sickness. - type: string - in: body - required: false - - name: sickness[reason] - description: Reason for the sickness. - type: string - in: body - required: false - - name: sickness[end_date] - description: End date of the sickness. - type: string - in: body - required: false - - name: sickness[review_notes] - description: Review notes for the sickness. - type: string - in: body - required: false - steps: - - stepId: update_sickness_record - description: Update sickness record - stepFunction: - functionName: request - parameters: - url: /sicknesses/${inputs.id} - method: put - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: sickness[company_sicknesstype_id] - value: "{{$.inputs['sickness[company_sicknesstype_id]']}}" - in: body - condition: "{{present($.inputs['sickness[company_sicknesstype_id]'])}}" - - name: sickness[start_date] - value: "{{$.inputs['sickness[start_date]']}}" - in: body - condition: "{{present($.inputs['sickness[start_date]'])}}" - - name: sickness[half_start] - value: "{{$.inputs['sickness[half_start]']}}" - in: body - condition: "{{present($.inputs['sickness[half_start]'])}}" - - name: sickness[half_start_am_pm] - value: "{{$.inputs['sickness[half_start_am_pm]']}}" - in: body - condition: "{{present($.inputs['sickness[half_start_am_pm]'])}}" - - name: sickness[half_end] - value: "{{$.inputs['sickness[half_end]']}}" - in: body - condition: "{{present($.inputs['sickness[half_end]'])}}" - - name: sickness[half_end_am_pm] - value: "{{$.inputs['sickness[half_end_am_pm]']}}" - in: body - condition: "{{present($.inputs['sickness[half_end_am_pm]'])}}" - - name: sickness[half_day] - value: "{{$.inputs['sickness[half_day]']}}" - in: body - condition: "{{present($.inputs['sickness[half_day]'])}}" - - name: sickness[status] - value: "{{$.inputs['sickness[status]']}}" - in: body - condition: "{{present($.inputs['sickness[status]'])}}" - - name: sickness[reason] - value: "{{$.inputs['sickness[reason]']}}" - in: body - condition: "{{present($.inputs['sickness[reason]'])}}" - - name: sickness[end_date] - value: "{{$.inputs['sickness[end_date]']}}" - in: body - condition: "{{present($.inputs['sickness[end_date]'])}}" - - name: sickness[review_notes] - value: "{{$.inputs['sickness[review_notes]']}}" - in: body - condition: "{{present($.inputs['sickness[review_notes]'])}}" - response: - collection: false - result: - data: $.steps.update_sickness_record.output.data - - actionId: delete_sickness - categories: - - hris - actionType: delete - label: Delete Sickness - description: Delete a specific sickness record by ID - details: "Deletes a sickness record from BreatheHR. Requires sickness ID. This action permanently removes the sickness record. Returns a success indicator." - inputs: - - name: id - description: Sickness ID. - type: number - in: path - required: true - steps: - - stepId: delete_sickness_record - description: Delete sickness record - stepFunction: - functionName: request - parameters: - url: /sicknesses/${inputs.id} - method: delete - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.delete_sickness_record.output.data - - actionId: list_working_patterns - categories: - - hris - actionType: list - label: List Working Patterns - description: Get a list of working patterns - details: "Returns a paginated list of all working patterns in the BreatheHR system. Supports pagination with page and per_page. Returns working pattern data including ID, name, type, hours, and other working pattern properties." - steps: - - stepId: get_all_working_patterns - description: Get working patterns request - stepFunction: - functionName: request - parameters: - url: /working_patterns - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_all_working_patterns.output.data - - actionId: list_absences - categories: - - hris - actionType: list - label: List Absences - description: Get a list of absences - details: "Returns a paginated list of all absences in the BreatheHR system. Supports filtering by type (Holiday or OtherLeave), other_leave_reason_id, exclude_cancelled_absences flag, employee_id, department_id, start_date, end_date, rotacloud flag, and pagination with page and per_page. Returns absence data including ID, employee_id, type, start_date, end_date, status, and other absence properties." - inputs: - - name: type - description: Type of absences to return, 'Holiday' or 'OtherLeave'. - type: string - in: query - required: false - - name: other_leave_reason_id - description: ID of other leave reason to filter by. - type: number - in: query - required: false - - name: exclude_cancelled_absences - description: Don't return cancelled absences. - type: boolean - in: query - required: false - - name: employee_id - description: Return absences for this employee only. - type: number - in: query - required: false - - name: department_id - description: Return absences for this department only. - type: number - in: query - required: false - - name: start_date - description: Return absences on or after this date. - type: string - in: query - required: false - - name: end_date - description: Return absences on or before this date. - type: string - in: query - required: false - - name: rotacloud - description: Return absences where rotacloud is not excluded from integration. - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_absences - description: Get absences request - stepFunction: - functionName: request - parameters: - url: /absences - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: type - value: '{{$.inputs.type}}' - in: query - condition: '{{present($.inputs.type)}}' - - name: other_leave_reason_id - value: '{{$.inputs.other_leave_reason_id}}' - in: query - condition: '{{present($.inputs.other_leave_reason_id)}}' - - name: exclude_cancelled_absences - value: '{{$.inputs.exclude_cancelled_absences}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_absences)}}' - - name: employee_id - value: '{{$.inputs.employee_id}}' - in: query - condition: '{{present($.inputs.employee_id)}}' - - name: department_id - value: '{{$.inputs.department_id}}' - in: query - condition: '{{present($.inputs.department_id)}}' - - name: start_date - value: '{{$.inputs.start_date}}' - in: query - condition: '{{present($.inputs.start_date)}}' - - name: end_date - value: '{{$.inputs.end_date}}' - in: query - condition: '{{present($.inputs.end_date)}}' - - name: rotacloud - value: '{{$.inputs.rotacloud}}' - in: query - condition: '{{present($.inputs.rotacloud)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_absences.output.data - - actionId: cancel_absence - categories: - - hris - actionType: update - label: Cancel Absence - description: Cancel a specific absence by ID - details: "Cancels an existing absence. Requires absence ID. This action marks the absence as cancelled. Returns a success indicator." - inputs: - - name: id - description: ID of absence to cancel. - type: number - in: path - required: true - - name: reason - description: Reason for cancellation. - type: string - in: body - required: false - steps: - - stepId: cancel_absence_request - description: Cancel absence request - stepFunction: - functionName: request - parameters: - url: /absences/${inputs.id}/cancel - method: post - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: Content-Type - value: application/x-www-form-urlencoded - in: headers - - name: reason - value: '{{$.inputs.reason}}' - in: body - condition: '{{present($.inputs.reason)}}' - response: - collection: false - result: - data: $.steps.cancel_absence_request.output.data - - actionId: get_account - categories: - - hris - actionType: get - label: Get Account Details - description: Get account details - details: "Returns account details for the BreatheHR account. Includes account information such as company name, settings, and other account properties." - steps: - - stepId: get_account_details - description: Get account details request - stepFunction: - functionName: request - parameters: - url: /account - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: false - result: - data: $.steps.get_account_details.output.data - - actionId: list_benefits - categories: - - hris - actionType: list - label: List Benefits - description: Get a list of benefits - details: "Returns a paginated list of all benefits in the BreatheHR system. Supports pagination with page and per_page. Returns benefit data including ID, name, type, and other benefit properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_benefits - description: Get benefits request - stepFunction: - functionName: request - parameters: - url: /benefits - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_benefits.output.data - - actionId: list_bonuses - categories: - - hris - actionType: list - label: List Bonuses - description: Get a list of bonuses - details: "Returns a paginated list of all bonuses in the BreatheHR system. Supports pagination with page and per_page. Returns bonus data including ID, employee_id, type, amount, date, and other bonus properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_bonuses - description: Get bonuses request - stepFunction: - functionName: request - parameters: - url: /bonuses - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_bonuses.output.data - - actionId: list_company_documents - categories: - - hris - actionType: list - label: List Company Documents - description: Get a list of company documents - details: "Returns a paginated list of all company documents in the BreatheHR system. Supports pagination with page and per_page. Returns document data including ID, name, type, and other document properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_company_documents - description: Get company documents request - stepFunction: - functionName: request - parameters: - url: /company_documents - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_company_documents.output.data - - actionId: list_departments - categories: - - hris - actionType: list - label: List Departments - description: Get a list of departments - details: "Returns a paginated list of all departments in the BreatheHR system. Supports pagination with page and per_page. Returns department data including ID, name, and other department properties." - inputs: - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_departments - description: Get departments request - stepFunction: - functionName: request - parameters: - url: /departments - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_departments.output.data - - actionId: list_department_absences - categories: - - hris - actionType: get - label: Get Department Absences - description: Get absences for a specific department by ID - details: "Returns a paginated list of absences for a specific department. Requires department ID. Supports optional filtering to exclude cancelled absences and pagination with page and per_page parameters. Returns absence data including dates, reasons, and other absence properties." - inputs: - - name: id - description: ID of department to return absences for. - type: string - in: path - required: true - - name: exclude_cancelled_absences - description: Don't return cancelled leave requests. - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_department_absences - description: Get department absences request - stepFunction: - functionName: request - parameters: - url: /departments/${inputs.id}/absences - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: exclude_cancelled_absences - value: '{{$.inputs.exclude_cancelled_absences}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_absences)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_department_absences.output.data - - actionId: list_department_benefits - categories: - - hris - actionType: get - label: Get Department Benefits - description: Get benefits for a specific department by ID - details: "Returns a paginated list of benefits for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns benefit data including benefit types, coverage details, and other benefit properties." - inputs: - - name: id - description: The ID of the department whose benefits to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_department_benefits - description: Get department benefits request - stepFunction: - functionName: request - parameters: - url: /departments/${inputs.id}/benefits - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_department_benefits.output.data - - actionId: list_department_bonuses - categories: - - hris - actionType: get - label: Get Department Bonuses - description: Get bonuses for a specific department by ID - details: "Returns a paginated list of bonuses for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns bonus data including amounts, dates, reasons, and other bonus properties." - inputs: - - name: id - description: The ID of the department whose bonuses to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_department_bonuses - description: Get department bonuses request - stepFunction: - functionName: request - parameters: - url: /departments/${inputs.id}/bonuses - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_department_bonuses.output.data - - actionId: list_department_leave_requests - categories: - - hris - actionType: get - label: Get Department Leave Requests - description: Get leave requests for a specific department by ID - details: "Returns a paginated list of leave requests for a specific department. Requires department ID. Supports optional filtering to exclude cancelled requests and pagination with page and per_page parameters. Returns leave request data including dates, status, and other request properties." - inputs: - - name: id - description: ID of department to return leave requests for. - type: string - in: path - required: true - - name: exclude_cancelled_requests - description: Don't return cancelled leave requests. - type: boolean - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_department_leave_requests - description: Get department leave requests request - stepFunction: - functionName: request - parameters: - url: /departments/${inputs.id}/leave_requests - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: exclude_cancelled_requests - value: '{{$.inputs.exclude_cancelled_requests}}' - in: query - condition: '{{present($.inputs.exclude_cancelled_requests)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_department_leave_requests.output.data - - actionId: list_department_salaries - categories: - - hris - actionType: get - label: Get Department Salaries - description: Get salaries for a specific department by ID - details: "Returns a paginated list of salaries for a specific department. Requires department ID. Supports pagination with page and per_page parameters. Returns salary data including amounts, effective dates, and other salary properties." - inputs: - - name: id - description: The ID of the department whose salaries to retrieve. - type: string - in: path - required: true - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_department_salaries - description: Get department salaries request - stepFunction: - functionName: request - parameters: - url: /departments/${inputs.id}/salaries - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_department_salaries.output.data - - actionId: list_divisions - categories: - - hris - actionType: list - label: List Divisions - description: Get a list of divisions - details: "Returns a paginated list of all divisions in the BreatheHR system. Supports pagination with page and per_page. Returns division data including ID, name, and other division properties." - steps: - - stepId: get_all_divisions - description: Get divisions request - stepFunction: - functionName: request - parameters: - url: /divisions - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - response: - collection: true - result: - data: $.steps.get_all_divisions.output.data - - actionId: list_employee_jobs - categories: - - hris - actionType: list - label: List Employee Jobs - description: Get a list of employee jobs - details: "Returns a paginated list of all employee jobs in the BreatheHR system. Supports filtering by employee_id and pagination with page and per_page. Returns employee job data including ID, employee_id, job_title, start_date, end_date, and other job properties." - inputs: - - name: employee_id - description: Return jobs for this employee only. - type: number - in: query - required: false - - name: page - description: Page of results to fetch. - type: number - in: query - required: false - - name: per_page - description: Number of results to return per page. - type: number - in: query - required: false - steps: - - stepId: get_all_employee_jobs - description: Get employee jobs request - stepFunction: - functionName: request - parameters: - url: /employee_jobs - method: get - args: - - name: X-Api-Key - value: $.credentials.apiKey - in: headers - - name: employee_id - value: '{{$.inputs.employee_id}}' - in: query - condition: '{{present($.inputs.employee_id)}}' - - name: page - value: '{{$.inputs.page}}' - in: query - condition: '{{present($.inputs.page)}}' - - name: per_page - value: '{{$.inputs.per_page}}' - in: query - condition: '{{present($.inputs.per_page)}}' - response: - collection: true - result: - data: $.steps.get_all_employee_jobs.output.data From 27c4f04ec61f0ff777e53f4ee8dc099da3a07431 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 11:50:15 +0000 Subject: [PATCH 11/13] fix: plugin fixes --- .../references/connector-patterns.md | 2 +- .../skills/setup-connector/SKILL.md | 4 ++-- .../skills/validate-connector/SKILL.md | 16 +++++++++------- .../references/connector-patterns.md | 2 +- .../skills/choose-schema/SKILL.md | 4 ++-- .../skills/import-schema/SKILL.md | 4 ++-- .../skills/map-fields/SKILL.md | 2 +- .../skills/scope-actions/SKILL.md | 5 +++-- .../skills/test-connector/SKILL.md | 2 +- examples/ashby/ashby.candidates.s1.partial.yaml | 4 ++-- 10 files changed, 24 insertions(+), 21 deletions(-) diff --git a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md index 4de1dd8..1b0aa30 100644 --- a/.claude/plugins/stackone-connector-builder/references/connector-patterns.md +++ b/.claude/plugins/stackone-connector-builder/references/connector-patterns.md @@ -535,4 +535,4 @@ result: | Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | | Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | -Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation), where `` matches a `configFields` or `setupFields` key in the authentication block. +Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation). The `` can be a `configFields` or `setupFields` key from the authentication block, or an auth-managed credential like `accessToken` or `refreshToken` (set automatically by OAuth flows). diff --git a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md index 69f7bdd..cf1a1c1 100644 --- a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md @@ -61,9 +61,9 @@ If unavailable: **If CLI available, first check if a pullable connector exists:** ```bash -npx @stackone/cli list | grep -i {{provider}} +npx @stackone/cli list | grep -iw {{provider}} ``` -If found in the CLI list, pull it: +Use `-w` for word-boundary matching to avoid substring matches (e.g., `bamboo` matching `bamboohr`). If found in the CLI list, pull it: ```bash npx @stackone/cli pull {{provider}} ``` diff --git a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md index 67fcb5c..f57a2e1 100644 --- a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md @@ -49,13 +49,13 @@ Work through each action across all `{{provider}}.{{resource}}.s1.partial.yaml` 5. **`list` actions have pagination inputs (if supported)** — Actions named `list_*` should have pagination inputs (`page`, `per_page`, `offset`, `limit`, or provider-specific equivalent) with `required: false`, if the provider endpoint supports pagination. -6. **`get` actions reference `${inputs.id}` in URL** — Actions named `get_*` must have `${inputs.id}` in `entrypointUrl` and an `id` input with `required: true`. +6. **`get` actions reference `${inputs.id}` in step URL** — Actions named `get_*` must have `${inputs.id}` in the request step's `url` parameter and an `id` input with `required: true`. -7. **`create` actions use POST** — Actions named `create_*` must have `entrypointHttpMethod: POST` and a `body` input with `type: object` and `required: true`. +7. **`create` actions use POST** — Actions named `create_*` must have `method: post` in the request step. -8. **`update` actions use PATCH or PUT** — Actions named `update_*` must have `entrypointHttpMethod: PATCH` or `entrypointHttpMethod: PUT`. +8. **`update` actions use PATCH or PUT** — Actions named `update_*` must have `method: patch` or `method: put` in the request step. -9. **`delete` actions use DELETE** — Actions named `delete_*` must have `entrypointHttpMethod: DELETE`. +9. **`delete` actions use DELETE** — Actions named `delete_*` must have `method: delete` in the request step. 10. **All `$ref` paths in main connector file exist** — Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml`. For every `$ref` entry under `actions`, verify the referenced partial file exists on disk at the given relative path. @@ -73,14 +73,16 @@ For each error from the CLI output or the manual checklist, identify the cause a **`Missing required field: entrypointUrl`** -Cause: The action YAML block is missing the `entrypointUrl` key. +Cause: A non-custom action (e.g., `actionType: list`) is missing the `entrypointUrl` key. Note: this error does not apply to `actionType: custom` actions, which do not require `entrypointUrl`. -Fix — add the field: +Fix — add the field to the non-custom action: ```yaml - actionId: list_employees categories: - {{category}} - actionType: custom + actionType: list + entrypointUrl: /employees # <-- add this + entrypointHttpMethod: get # <-- add this label: List Employees description: List all employees steps: diff --git a/.claude/plugins/stackone-unified-builder/references/connector-patterns.md b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md index 4de1dd8..1b0aa30 100644 --- a/.claude/plugins/stackone-unified-builder/references/connector-patterns.md +++ b/.claude/plugins/stackone-unified-builder/references/connector-patterns.md @@ -535,4 +535,4 @@ result: | Subdomain | `${PROVIDER_SUBDOMAIN}` | `${BAMBOOHR_SUBDOMAIN}` | | Account ID | `${PROVIDER_ACCOUNT_ID}` | `${NETSUITE_ACCOUNT_ID}` | -Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation), where `` matches a `configFields` or `setupFields` key in the authentication block. +Reference inside the connector using `$.credentials.` (JSONPath) or `${credentials.}` (string interpolation). The `` can be a `configFields` or `setupFields` key from the authentication block, or an auth-managed credential like `accessToken` or `refreshToken` (set automatically by OAuth flows). diff --git a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md index 785fcee..4f0ed10 100644 --- a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md @@ -15,7 +15,7 @@ Read `.connector-build-session.json` from the project root if it exists. If it exists and both `schema` and `provider` are already set, greet the builder: > "Resuming session — schema already set to `{{schema}}` ({{schema_source}}) for provider `{{provider}}`. Run `/check-connector` to continue, or type `reset` to start over." -If it exists and `schema` is set but `provider` is missing (e.g., from an imported schema), proceed to Step 1 to collect the provider name. +If it exists and `schema` is set but `provider` is missing (e.g., from an imported schema), proceed to Step 1 to collect the provider name only, then skip directly to `/check-connector` — do not re-run schema selection since the schema is already saved. If it does not exist or `schema` is empty, proceed below. @@ -69,7 +69,7 @@ Read the schema file. Use the **Endpoints** section to show the builder what API Ask: > "Which resource(s) do you need to expose? Here's what's available in `{{category}}`:" -List the available resources from the schema file (e.g., `Employee`, `Employment`, `HRISDepartment`). When saving to session, convert model names to lowercase resource slugs (e.g., `Employee` → `employees`, `HRISDepartment` → `departments`) since later steps use slugs for action names and file paths. +List the available resources by deriving them from the **Endpoints** section of the schema file (not the Models section, which includes helper types that are not buildable resources). Use the resource slugs from the endpoint paths (e.g., `/unified/hris/employees` → `employees`, `/unified/hris/groups/departments` → `departments`). Save these slugs directly to session since later steps use them for action names and file paths. Once resources are chosen, show the fields table for each from the loaded schema file. diff --git a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md index 579184a..847b469 100644 --- a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md @@ -169,9 +169,9 @@ Once confirmed, save to `.connector-build-session.json`: > > These fields will be used as your `targetFieldKey` names in `/map-fields`. The next step is deciding which provider operations to build. > -> - If you haven't set a provider yet: tell me which provider you're building for, then run `/check-connector` +> - If you haven't set a provider yet: tell me which provider you're building for (ask directly — do not redirect to `/choose-schema`), then run `/check-connector` > - If provider is already set but connector hasn't been pulled/scaffolded: run `/check-connector` > - If provider + connector + actions are already set: run `/map-fields` directly" Update session: -- `session_step` → `"check-connector"` (if provider is set but connector not yet pulled/scaffolded) or `"choose-schema"` (if provider is not set — the choose-schema flow will collect the provider name first since `provider` is missing) +- `session_step` → `"map-fields"` (if provider, connector, and action_scope are all set) or `"check-connector"` (if provider is set but connector not yet pulled/scaffolded) or `"choose-schema"` (if provider is not set — ask for provider name directly before redirecting) diff --git a/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md index ddfa0a5..b209d25 100644 --- a/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/map-fields/SKILL.md @@ -48,7 +48,7 @@ Getting a real sample response makes field mapping accurate. **If `cli_available` is true in session and the action already exists in the connector YAML:** ```bash -npx @stackone/cli test {{provider}} list_{{resource}} --debug +npx @stackone/cli test {{provider}} unified_list_{{resource}} --debug ``` This shows the raw JSON before any mapping. Use the output to identify exact field paths. diff --git a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md index 49a6482..0367fd8 100644 --- a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md @@ -54,9 +54,10 @@ Save the use case description to session as `use_case`. ## Step 3: Present a recommended scope -First, check if indexed provider actions are available: +First, resolve the provider key and check if indexed provider actions are available: ``` -get_provider_actions("{{provider_key}}") +map_provider_key("{{provider}}") // Resolve to exact provider key +get_provider_actions("{{resolved_provider_key}}") ``` If indexed actions exist, use them to inform the recommendation — only suggest actions the provider's API actually supports. If no indexed data is available, recommend based on the session `resources` and use case, but note that support should be verified during testing. diff --git a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md index 57eeaf1..ba96a96 100644 --- a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md @@ -46,7 +46,7 @@ Use `test_actions` for automated testing: ``` test_actions({ provider: "{{provider}}", - actions: ["unified_list_{{resource}}", "unified_get_{{resource}}", "unified_create_{{resource}}", "unified_update_{{resource}}", "unified_delete_{{resource}}"] + actions: ["unified_list_{{resource}}", "unified_get_{{resource}}", "unified_create_{{resource}}", "unified_update_{{resource}}", "unified_delete_{{resource}}"], // Include only actions that exist in action_scope }) ``` diff --git a/examples/ashby/ashby.candidates.s1.partial.yaml b/examples/ashby/ashby.candidates.s1.partial.yaml index 658f6f2..7e11641 100644 --- a/examples/ashby/ashby.candidates.s1.partial.yaml +++ b/examples/ashby/ashby.candidates.s1.partial.yaml @@ -51,7 +51,7 @@ actions: response: collection: true result: - data: $.steps.fetch_candidates.output.data + data: $.steps.fetch_candidates.output.data.results nextCursor: $.steps.fetch_candidates.output.data.nextCursor syncToken: $.steps.fetch_candidates.output.data.syncToken moreDataAvailable: $.steps.fetch_candidates.output.data.moreDataAvailable @@ -543,7 +543,7 @@ actions: response: collection: true result: - data: $.steps.fetch_notes.output.data + data: $.steps.fetch_notes.output.data.results nextCursor: $.steps.fetch_notes.output.data.nextCursor syncToken: $.steps.fetch_notes.output.data.syncToken moreDataAvailable: $.steps.fetch_notes.output.data.moreDataAvailable From d0ff0a5bb259c3de5a245b3dbe5a1faaec3a312f Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 13:25:53 +0000 Subject: [PATCH 12/13] fix: More comments. --- .../skills/setup-connector/SKILL.md | 4 ++-- .../skills/validate-connector/SKILL.md | 24 +------------------ .../skills/choose-schema/SKILL.md | 2 +- .../skills/import-schema/SKILL.md | 11 +++++---- .../skills/scope-actions/SKILL.md | 6 ++--- 5 files changed, 14 insertions(+), 33 deletions(-) diff --git a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md index cf1a1c1..6b3ec99 100644 --- a/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/setup-connector/SKILL.md @@ -61,9 +61,9 @@ If unavailable: **If CLI available, first check if a pullable connector exists:** ```bash -npx @stackone/cli list | grep -iw {{provider}} +npx @stackone/cli list | grep -ix "{{provider}}" ``` -Use `-w` for word-boundary matching to avoid substring matches (e.g., `bamboo` matching `bamboohr`). If found in the CLI list, pull it: +Use `-x` for full-line matching to avoid false positives from substring or hyphenated-name matches. If found in the CLI list, pull it: ```bash npx @stackone/cli pull {{provider}} ``` diff --git a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md index f57a2e1..9ec7b57 100644 --- a/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/validate-connector/SKILL.md @@ -73,29 +73,7 @@ For each error from the CLI output or the manual checklist, identify the cause a **`Missing required field: entrypointUrl`** -Cause: A non-custom action (e.g., `actionType: list`) is missing the `entrypointUrl` key. Note: this error does not apply to `actionType: custom` actions, which do not require `entrypointUrl`. - -Fix — add the field to the non-custom action: -```yaml -- actionId: list_employees - categories: - - {{category}} - actionType: list - entrypointUrl: /employees # <-- add this - entrypointHttpMethod: get # <-- add this - label: List Employees - description: List all employees - steps: - - stepId: fetch_employees - description: Retrieve all employees - stepFunction: - functionName: request - parameters: - url: '/employees' - method: get - result: - data: $.steps.fetch_employees.output.data -``` +Cause: This error applies to non-custom actions (`actionType: list`, `get`, etc.) that are missing the `entrypointUrl` key. This error should not occur in this plugin's workflow since all actions use `actionType: custom`, which does not require `entrypointUrl`. If you see this error, check that the action has `actionType: custom`. --- diff --git a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md index 4f0ed10..a698132 100644 --- a/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/choose-schema/SKILL.md @@ -69,7 +69,7 @@ Read the schema file. Use the **Endpoints** section to show the builder what API Ask: > "Which resource(s) do you need to expose? Here's what's available in `{{category}}`:" -List the available resources by deriving them from the **Endpoints** section of the schema file (not the Models section, which includes helper types that are not buildable resources). Use the resource slugs from the endpoint paths (e.g., `/unified/hris/employees` → `employees`, `/unified/hris/groups/departments` → `departments`). Save these slugs directly to session since later steps use them for action names and file paths. +List the available resources by deriving them from the **Endpoints** section of the schema file (not the Models section, which includes helper types that are not buildable resources). Only include canonical resource slugs that correspond to top-level CRUD-able entities — filter out operation-specific sub-paths like `/invite`, `/download`, `/upload`, `/batch`, or `/callback`. Use the resource slugs from the endpoint paths (e.g., `/unified/hris/employees` → `employees`, `/unified/hris/groups/departments` → `departments`). Save these slugs directly to session since later steps use them for action names and file paths. Once resources are chosen, show the fields table for each from the loaded schema file. diff --git a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md index 847b469..30e1fb4 100644 --- a/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/import-schema/SKILL.md @@ -169,9 +169,12 @@ Once confirmed, save to `.connector-build-session.json`: > > These fields will be used as your `targetFieldKey` names in `/map-fields`. The next step is deciding which provider operations to build. > -> - If you haven't set a provider yet: tell me which provider you're building for (ask directly — do not redirect to `/choose-schema`), then run `/check-connector` -> - If provider is already set but connector hasn't been pulled/scaffolded: run `/check-connector` -> - If provider + connector + actions are already set: run `/map-fields` directly" +> - If you haven't set a provider yet: tell me which provider you're building for" +> +> If the builder provides a provider name, **save it to session as `provider` immediately**, then run `/check-connector`. +> If provider is already set but connector hasn't been pulled/scaffolded: run `/check-connector`. +> If provider + connector + actions are already set: run `/map-fields` directly. Update session: -- `session_step` → `"map-fields"` (if provider, connector, and action_scope are all set) or `"check-connector"` (if provider is set but connector not yet pulled/scaffolded) or `"choose-schema"` (if provider is not set — ask for provider name directly before redirecting) +- `provider` → the builder's answer (if just collected) +- `session_step` → `"map-fields"` (if provider, connector, and action_scope are all set) or `"check-connector"` (if provider is set but connector not yet pulled/scaffolded) or `"choose-schema"` (if provider is still not set after prompting) diff --git a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md index 0367fd8..e54a402 100644 --- a/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/scope-actions/SKILL.md @@ -54,10 +54,10 @@ Save the use case description to session as `use_case`. ## Step 3: Present a recommended scope -First, resolve the provider key and check if indexed provider actions are available: +First, resolve the provider key (from session `provider`) and check if indexed provider actions are available: ``` -map_provider_key("{{provider}}") // Resolve to exact provider key -get_provider_actions("{{resolved_provider_key}}") +provider_key = map_provider_key(session.provider) +get_provider_actions(provider_key) ``` If indexed actions exist, use them to inform the recommendation — only suggest actions the provider's API actually supports. If no indexed data is available, recommend based on the session `resources` and use case, but note that support should be verified during testing. From c7d02eb648cb807e2b5aa9f47267ea5e6fcdd595 Mon Sep 17 00:00:00 2001 From: cameron-carlin Date: Mon, 23 Mar 2026 14:59:33 +0000 Subject: [PATCH 13/13] fix: Update auth setup description The description for the `/configure-auth` command in the `stackone-connector-builder` skill was updated to be more specific about the supported authentication types (custom or OAuth2). --- .../stackone-connector-builder/skills/build-config/SKILL.md | 4 ++-- .../skills/stackone-connector-builder/SKILL.md | 4 ++-- .../references/field-mapping-guide.md | 2 -- .../stackone-unified-builder/references/schemas/hris.md | 2 +- .../stackone-unified-builder/references/schemas/iam.md | 2 +- .../stackone-unified-builder/skills/test-connector/SKILL.md | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md index 70f01f0..24331ac 100644 --- a/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/build-config/SKILL.md @@ -179,8 +179,8 @@ Read `src/configs/{{provider}}/{{provider}}.connector.s1.yaml` and add a `$ref` ```yaml actions: - $ref: {{provider}}.employees - $ref: {{provider}}.departments + - $ref: {{provider}}.employees.s1.partial.yaml + - $ref: {{provider}}.departments.s1.partial.yaml ``` --- diff --git a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md index d7963bc..d3efae5 100644 --- a/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md +++ b/.claude/plugins/stackone-connector-builder/skills/stackone-connector-builder/SKILL.md @@ -15,7 +15,7 @@ For connectors that normalise data to a standard schema, use `/build-unified-con | Step | Command | What it does | |------|---------|-------------| | 1 | `/setup-connector` | Provider name, index check, CLI pull or scaffold | -| 2 | `/configure-auth` | Set up authentication (API key, OAuth2, Basic Auth) | +| 2 | `/configure-auth` | Set up authentication (custom or OAuth2) | | 3 | `/discover-actions` | Choose scoped actions or discover everything | | 4 | `/build-config` | Generate YAML for all confirmed actions | | 5 | `/validate-connector` | Validate the YAML config | @@ -95,7 +95,7 @@ Execute `/test-connector` logic. Saves: `tested: true`, `test_artifacts`, `compl ## Rules -- Do not write YAML until Step 4 (`build-config`) +- Do not write action YAML until Step 4 (`build-config`) — auth config written in Step 2 is the exception - Do not run tests until Step 5 (`validate-connector`) passes - Always clean up test records — log anything that cannot be removed - Always run `scramble_credentials` after testing diff --git a/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md index 4e98c83..11f4e75 100644 --- a/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md +++ b/.claude/plugins/stackone-unified-builder/references/field-mapping-guide.md @@ -1,4 +1,3 @@ -````markdown # Field Mapping Guide Detailed reference for writing `fieldConfigs` in unified connectors. @@ -318,4 +317,3 @@ This shows exactly what the provider returns before any mapping. Use it to find - Do NOT use `objectMapping` — it is not supported by the validator - Use the inline block scalar `expression: |` pattern instead - In `group_data` context, confirm the step ID prefix is correct -```` diff --git a/.claude/plugins/stackone-unified-builder/references/schemas/hris.md b/.claude/plugins/stackone-unified-builder/references/schemas/hris.md index ca374fd..700bd3d 100644 --- a/.claude/plugins/stackone-unified-builder/references/schemas/hris.md +++ b/.claude/plugins/stackone-unified-builder/references/schemas/hris.md @@ -728,7 +728,7 @@ | `remote_policy_id` | `string` | Provider's unique identifier of the time off policy id associated with this balance | | `policy` | `TimeOffPolicies` | The time off policy associated with this balance | | `current_balance` | `number` | The current numeric balance for the associated employee and time off policy | -| `initial_balance` | `number` | The initial numeric balance for the associated employee and time off policy as of the balance sta... | +| `initial_balance` | `number` | The initial numeric balance for the associated employee and time off policy as of the balance start date | | `balance_unit` | `TimeOffBalanceUnitEnum` | The duration unit of the current balance | | `balance_start_date` | `string` | The date of when the initial balance quantity was set | | `balance_expiry_date` | `string` | The date of when the current balance expires | diff --git a/.claude/plugins/stackone-unified-builder/references/schemas/iam.md b/.claude/plugins/stackone-unified-builder/references/schemas/iam.md index 4ee8714..8e4bb17 100644 --- a/.claude/plugins/stackone-unified-builder/references/schemas/iam.md +++ b/.claude/plugins/stackone-unified-builder/references/schemas/iam.md @@ -131,7 +131,7 @@ | `last_active_at` | `string` | The date this user was last active | | `last_login_at` | `string` | The date this user last logged in | | `created_at` | `string` | The date the user was created | -| `updated_at` | `string` | The date the user was created | +| `updated_at` | `string` | The date the user was last updated | | `multi_factor_enabled` | `array` | The list of Multi-Factor Authentication (MFA) types enabled for the user. | | `avatar` | `File` | The user's avatar data. This generally contains a URL within this property's 'contents' array. | diff --git a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md index ba96a96..bbe4b46 100644 --- a/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md +++ b/.claude/plugins/stackone-unified-builder/skills/test-connector/SKILL.md @@ -88,7 +88,7 @@ npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} **Field is null or missing in output:** > "Field `{{field}}` is missing. Let's debug: -> 1. Run with `--debug` to see the raw provider response: `npx @stackone/cli test {{provider}} {{action}} --debug` +> 1. Run with `--debug` to see the raw provider response: `npx @stackone/cli test {{provider}} unified_{{action}}_{{resource}} --debug` > 2. Find `{{field}}` in the raw response — what is the actual JSONPath? > 3. Update the `expression` in `fieldConfigs` to match."