Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dbc7fdddf | ||
|
|
387cc977fa | ||
|
|
c0bc62c592 | ||
|
|
010435c53b | ||
|
|
46ffc4904e | ||
|
|
3a1b1a8032 | ||
|
|
fc07922536 | ||
|
|
3838b6e892 | ||
|
|
0ac05397eb | ||
|
|
e3d0e74cc4 | ||
|
|
8b57476957 | ||
|
|
4309d0619a | ||
|
|
cbfab1ceaa | ||
|
|
4f40c4ce3e | ||
|
|
d33acf426d | ||
|
|
3c8bb4076c | ||
|
|
f8f3758649 | ||
|
|
cf233bb497 | ||
|
|
d7da35ba0b | ||
|
|
d6ec115348 | ||
|
|
3f508e445f | ||
|
|
316bc8cdcc | ||
|
|
d889f32697 | ||
|
|
28af223a9f | ||
|
|
a54dcbe949 | ||
|
|
0b9019d9a2 |
@@ -16,34 +16,17 @@ User arguments: $ARGUMENTS
|
||||
Read before analyzing:
|
||||
1. https://react.dev/reference/react/useCallback — official docs on when useCallback is actually needed
|
||||
|
||||
## The one rule that matters
|
||||
|
||||
`useCallback` is only useful when **something observes the reference**. Ask: does anything care if this function gets a new identity on re-render?
|
||||
|
||||
Observers that care about reference stability:
|
||||
- A `useEffect` that lists the function in its deps array
|
||||
- A `useMemo` that lists the function in its deps array
|
||||
- Another `useCallback` that lists the function in its deps array
|
||||
- A child component wrapped in `React.memo` that receives the function as a prop
|
||||
|
||||
If none of those apply — if the function is only called inline, or passed to a non-memoized child, or assigned to a native element event — the reference is unobserved and `useCallback` adds overhead with zero benefit.
|
||||
|
||||
## Anti-patterns to detect
|
||||
|
||||
1. **No observer tracks the reference**: The function is only called inline in the same component, or passed to a non-memoized child, or used as a native element handler (`<button onClick={fn}>`). Nothing re-runs or bails out based on reference identity. Remove `useCallback`.
|
||||
2. **useCallback with deps that change every render**: If a dep is a plain object/array created inline, or state that changes on every interaction, memoization buys nothing — the function gets a new identity anyway.
|
||||
3. **useCallback on handlers passed only to native elements**: `<button onClick={fn}>` — React never does reference equality on native element props. No benefit.
|
||||
4. **useCallback wrapping functions that return new objects/arrays**: Stable function identity, unstable return value — memoization is at the wrong level. Use `useMemo` on the return value instead, or restructure.
|
||||
5. **useCallback with empty deps when deps are needed**: Stale closure — reads initial values forever. This is a correctness bug, not just a performance issue.
|
||||
6. **Pairing useCallback + React.memo on trivially cheap renders**: If the child renders in < 1ms and re-renders rarely, the memo infrastructure costs more than it saves.
|
||||
1. **useCallback on functions not passed as props or deps**: No benefit if only called within the same component.
|
||||
2. **useCallback with deps that change every render**: Memoization is wasted.
|
||||
3. **useCallback on handlers passed to native elements**: `<button onClick={fn}>` doesn't benefit from stable references.
|
||||
4. **useCallback wrapping functions that return new objects/arrays**: Memoization at the wrong level.
|
||||
5. **useCallback with empty deps when deps are needed**: Stale closures.
|
||||
6. **Pairing useCallback + React.memo unnecessarily**: Only optimize when you've measured a problem.
|
||||
7. **useCallback in hooks that don't need stable references**: Not every hook return needs memoization.
|
||||
|
||||
## Patterns that ARE correct — do not flag
|
||||
|
||||
- `useCallback` whose result is in a `useEffect` dep array — prevents the effect from re-running on every render
|
||||
- `useCallback` whose result is in a `useMemo` dep array — prevents the memo from recomputing on every render
|
||||
- `useCallback` whose result is a dep of another `useCallback` — stabilises a callback chain
|
||||
- `useCallback` passed to a `React.memo`-wrapped child — the whole point of the pattern
|
||||
- This codebase's ref pattern: `useRef` + callback with empty deps that reads the ref inside — correct, do not flag
|
||||
Note: This codebase uses a ref pattern for stable callbacks (`useRef` + empty deps). That pattern is correct — don't flag it.
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
# Global Standards
|
||||
|
||||
## Logging
|
||||
Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`. Inside API routes wrapped with `withRouteHandler`, loggers automatically include the request ID.
|
||||
|
||||
## API Route Handlers
|
||||
All API route handlers must be wrapped with `withRouteHandler` from `@/lib/core/utils/with-route-handler`. Never export a bare `async function GET/POST/...` — always use `export const METHOD = withRouteHandler(...)`.
|
||||
Import `createLogger` from `sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
|
||||
|
||||
## Comments
|
||||
Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
|
||||
@@ -13,7 +10,7 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
|
||||
Never update global styles. Keep all styling local to components.
|
||||
|
||||
## ID Generation
|
||||
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@sim/utils/id`:
|
||||
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`:
|
||||
|
||||
- `generateId()` — UUID v4, use by default
|
||||
- `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers
|
||||
@@ -27,32 +24,11 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
const id = crypto.randomUUID()
|
||||
|
||||
// ✓ Good
|
||||
import { generateId, generateShortId } from '@sim/utils/id'
|
||||
import { generateId, generateShortId } from '@/lib/core/utils/uuid'
|
||||
const uuid = generateId()
|
||||
const shortId = generateShortId()
|
||||
const tiny = generateShortId(8)
|
||||
```
|
||||
|
||||
## Common Utilities
|
||||
Use shared helpers from `@sim/utils` instead of writing inline implementations:
|
||||
|
||||
- `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))`
|
||||
- `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))`
|
||||
- `toError(value).message` — get error message safely. Never write `e instanceof Error ? e.message : String(e)`
|
||||
|
||||
```typescript
|
||||
// ✗ Bad
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
const msg = error instanceof Error ? error.message : String(error)
|
||||
const err = error instanceof Error ? error : new Error(String(error))
|
||||
|
||||
// ✓ Good
|
||||
import { sleep } from '@sim/utils/helpers'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
await sleep(1000)
|
||||
const msg = toError(error).message
|
||||
const err = toError(error)
|
||||
```
|
||||
|
||||
## Package Manager
|
||||
Use `bun` and `bunx`, not `npm` and `npx`.
|
||||
|
||||
@@ -13,12 +13,8 @@ Use Vitest. Test files: `feature.ts` → `feature.test.ts`
|
||||
These modules are mocked globally — do NOT re-mock them in test files unless you need to override behavior:
|
||||
|
||||
- `@sim/db` → `databaseMock`
|
||||
- `@sim/db/schema` → `schemaMock`
|
||||
- `drizzle-orm` → `drizzleOrmMock`
|
||||
- `@sim/logger` → `loggerMock`
|
||||
- `@/lib/auth` → `authMock`
|
||||
- `@/lib/auth/hybrid` → `hybridAuthMock` (with default session-delegating behavior)
|
||||
- `@/lib/core/utils/request` → `requestUtilsMock`
|
||||
- `@/stores/console/store`, `@/stores/terminal`, `@/stores/execution/store`
|
||||
- `@/blocks/registry`
|
||||
- `@trigger.dev/sdk`
|
||||
@@ -106,6 +102,10 @@ vi.mock('@/lib/workspaces/utils', () => ({
|
||||
}))
|
||||
```
|
||||
|
||||
### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing`
|
||||
|
||||
These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead.
|
||||
|
||||
### Mock heavy transitive dependencies
|
||||
|
||||
If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them:
|
||||
@@ -135,129 +135,83 @@ await new Promise(r => setTimeout(r, 1))
|
||||
vi.useFakeTimers()
|
||||
```
|
||||
|
||||
## Centralized Mocks (prefer over local declarations)
|
||||
|
||||
`@sim/testing` exports ready-to-use mock modules for common dependencies. Import and pass directly to `vi.mock()` — no `vi.hoisted()` boilerplate needed. Each paired `*MockFns` object exposes the underlying `vi.fn()`s for per-test overrides.
|
||||
|
||||
| Module mocked | Import | Factory form |
|
||||
|---|---|---|
|
||||
| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` |
|
||||
| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` |
|
||||
| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` |
|
||||
| `@sim/audit` | `auditMock`, `auditMockFns` | `vi.mock('@sim/audit', () => auditMock)` |
|
||||
| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` |
|
||||
| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` |
|
||||
| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` |
|
||||
| `@/lib/core/config/env` | `envMock`, `createEnvMock(overrides)` | `vi.mock('@/lib/core/config/env', () => envMock)` |
|
||||
| `@/lib/core/config/feature-flags` | `featureFlagsMock` | `vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock)` |
|
||||
| `@/lib/core/config/redis` | `redisConfigMock`, `redisConfigMockFns` | `vi.mock('@/lib/core/config/redis', () => redisConfigMock)` |
|
||||
| `@/lib/core/security/encryption` | `encryptionMock`, `encryptionMockFns` | `vi.mock('@/lib/core/security/encryption', () => encryptionMock)` |
|
||||
| `@/lib/core/security/input-validation.server` | `inputValidationMock`, `inputValidationMockFns` | `vi.mock('@/lib/core/security/input-validation.server', () => inputValidationMock)` |
|
||||
| `@/lib/core/utils/request` | `requestUtilsMock`, `requestUtilsMockFns` | `vi.mock('@/lib/core/utils/request', () => requestUtilsMock)` |
|
||||
| `@/lib/core/utils/urls` | `urlsMock`, `urlsMockFns` | `vi.mock('@/lib/core/utils/urls', () => urlsMock)` |
|
||||
| `@/lib/execution/preprocessing` | `executionPreprocessingMock`, `executionPreprocessingMockFns` | `vi.mock('@/lib/execution/preprocessing', () => executionPreprocessingMock)` |
|
||||
| `@/lib/logs/execution/logging-session` | `loggingSessionMock`, `loggingSessionMockFns`, `LoggingSessionMock` | `vi.mock('@/lib/logs/execution/logging-session', () => loggingSessionMock)` |
|
||||
| `@/lib/workflows/orchestration` | `workflowsOrchestrationMock`, `workflowsOrchestrationMockFns` | `vi.mock('@/lib/workflows/orchestration', () => workflowsOrchestrationMock)` |
|
||||
| `@/lib/workflows/persistence/utils` | `workflowsPersistenceUtilsMock`, `workflowsPersistenceUtilsMockFns` | `vi.mock('@/lib/workflows/persistence/utils', () => workflowsPersistenceUtilsMock)` |
|
||||
| `@/lib/workflows/utils` | `workflowsUtilsMock`, `workflowsUtilsMockFns` | `vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock)` |
|
||||
| `@/lib/workspaces/permissions/utils` | `permissionsMock`, `permissionsMockFns` | `vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)` |
|
||||
| `@sim/db/schema` | `schemaMock` | `vi.mock('@sim/db/schema', () => schemaMock)` |
|
||||
## Mock Pattern Reference
|
||||
|
||||
### Auth mocking (API routes)
|
||||
|
||||
```typescript
|
||||
import { authMock, authMockFns } from '@sim/testing'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
const { mockGetSession } = vi.hoisted(() => ({
|
||||
mockGetSession: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/auth', () => authMock)
|
||||
vi.mock('@/lib/auth', () => ({
|
||||
auth: { api: { getSession: vi.fn() } },
|
||||
getSession: mockGetSession,
|
||||
}))
|
||||
|
||||
import { GET } from '@/app/api/my-route/route'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
|
||||
})
|
||||
// In tests:
|
||||
mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } })
|
||||
mockGetSession.mockResolvedValue(null) // unauthenticated
|
||||
```
|
||||
|
||||
Only define a local `vi.mock('@/lib/auth', ...)` if the module under test consumes exports outside the centralized shape (e.g., `auth.api.verifyOneTimeToken`, `auth.api.resetPassword`).
|
||||
|
||||
### Hybrid auth mocking
|
||||
|
||||
```typescript
|
||||
import { hybridAuthMock, hybridAuthMockFns } from '@sim/testing'
|
||||
const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({
|
||||
mockCheckSessionOrInternalAuth: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)
|
||||
vi.mock('@/lib/auth/hybrid', () => ({
|
||||
checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth,
|
||||
}))
|
||||
|
||||
// In tests:
|
||||
hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({
|
||||
mockCheckSessionOrInternalAuth.mockResolvedValue({
|
||||
success: true, userId: 'user-1', authType: 'session',
|
||||
})
|
||||
```
|
||||
|
||||
### Database chain mocking
|
||||
|
||||
Use the centralized `dbChainMock` + `dbChainMockFns` helpers — no `vi.hoisted()` or chain-wiring boilerplate needed.
|
||||
|
||||
```typescript
|
||||
import { dbChainMock, dbChainMockFns, resetDbChainMock } from '@sim/testing'
|
||||
const { mockSelect, mockFrom, mockWhere } = vi.hoisted(() => ({
|
||||
mockSelect: vi.fn(),
|
||||
mockFrom: vi.fn(),
|
||||
mockWhere: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@sim/db', () => dbChainMock)
|
||||
// Spread for custom exports: vi.mock('@sim/db', () => ({ ...dbChainMock, myTable: {...} }))
|
||||
vi.mock('@sim/db', () => ({
|
||||
db: { select: mockSelect },
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
resetDbChainMock() // only needed if tests use permanent (non-`Once`) overrides
|
||||
})
|
||||
|
||||
it('reads a row', async () => {
|
||||
dbChainMockFns.limit.mockResolvedValueOnce([{ id: '1', name: 'test' }])
|
||||
// exercise code that hits db.select().from().where().limit()
|
||||
expect(dbChainMockFns.where).toHaveBeenCalled()
|
||||
mockSelect.mockReturnValue({ from: mockFrom })
|
||||
mockFrom.mockReturnValue({ where: mockWhere })
|
||||
mockWhere.mockResolvedValue([{ id: '1', name: 'test' }])
|
||||
})
|
||||
```
|
||||
|
||||
**Default chains supported:**
|
||||
- `select()/selectDistinct()/selectDistinctOn() → from() → where()/innerJoin()/leftJoin() → where() → limit()/orderBy()/returning()/groupBy()/for()`
|
||||
- `insert() → values() → returning()/onConflictDoUpdate()/onConflictDoNothing()`
|
||||
- `update() → set() → where() → limit()/orderBy()/returning()/for()`
|
||||
- `delete() → where() → limit()/orderBy()/returning()/for()`
|
||||
- `db.execute()` resolves `[]`
|
||||
- `db.transaction(cb)` calls cb with `dbChainMock.db`
|
||||
|
||||
`.for('update')` (Postgres row-level locking) is supported on `where`
|
||||
builders. It returns a thenable with `.limit` / `.orderBy` / `.returning` /
|
||||
`.groupBy` attached, so both `await .where().for('update')` (terminal) and
|
||||
`await .where().for('update').limit(1)` (chained) work. Override the terminal
|
||||
result with `dbChainMockFns.for.mockResolvedValueOnce([...])`; for the chained
|
||||
form, mock the downstream terminal (e.g. `dbChainMockFns.limit.mockResolvedValueOnce([...])`).
|
||||
|
||||
All terminals default to `Promise.resolve([])`. Override per-test with `dbChainMockFns.<terminal>.mockResolvedValueOnce(...)`.
|
||||
|
||||
Use `resetDbChainMock()` in `beforeEach` only when tests replace wiring with `.mockReturnValue` / `.mockResolvedValue` (permanent). Tests using only `...Once` variants don't need it.
|
||||
|
||||
## @sim/testing Package
|
||||
|
||||
Always prefer over local test data.
|
||||
|
||||
| Category | Utilities |
|
||||
|----------|-----------|
|
||||
| **Module mocks** | See "Centralized Mocks" table above |
|
||||
| **Logger helpers** | `loggerMock`, `createMockLogger()`, `getLoggerCalls()`, `clearLoggerMocks()` |
|
||||
| **Database helpers** | `databaseMock`, `drizzleOrmMock`, `createMockDb()`, `createMockSql()`, `createMockSqlOperators()` |
|
||||
| **Fetch helpers** | `setupGlobalFetchMock()`, `createMockFetch()`, `createMockResponse()`, `mockFetchError()` |
|
||||
| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` |
|
||||
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` |
|
||||
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
|
||||
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
|
||||
| **Requests** | `createMockRequest()`, `createMockFormDataRequest()` |
|
||||
| **Requests** | `createMockRequest()`, `createEnvMock()` |
|
||||
|
||||
## Rules Summary
|
||||
|
||||
1. `@vitest-environment node` unless DOM is required
|
||||
2. Prefer centralized mocks from `@sim/testing` (see table above) over local `vi.hoisted()` + `vi.mock()` boilerplate
|
||||
3. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
|
||||
4. `vi.mock()` calls before importing mocked modules
|
||||
2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
|
||||
3. `vi.mock()` calls before importing mocked modules
|
||||
4. `@sim/testing` utilities over local mocks
|
||||
5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach`
|
||||
6. No `vi.importActual()` — mock everything explicitly
|
||||
7. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
|
||||
8. Use absolute imports in test files
|
||||
9. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`
|
||||
7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks
|
||||
8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
|
||||
9. Use absolute imports in test files
|
||||
10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`
|
||||
|
||||
@@ -17,7 +17,7 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
|
||||
Never update global styles. Keep all styling local to components.
|
||||
|
||||
## ID Generation
|
||||
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@sim/utils/id`:
|
||||
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`:
|
||||
|
||||
- `generateId()` — UUID v4, use by default
|
||||
- `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers
|
||||
@@ -31,32 +31,11 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
const id = crypto.randomUUID()
|
||||
|
||||
// ✓ Good
|
||||
import { generateId, generateShortId } from '@sim/utils/id'
|
||||
import { generateId, generateShortId } from '@/lib/core/utils/uuid'
|
||||
const uuid = generateId()
|
||||
const shortId = generateShortId()
|
||||
const tiny = generateShortId(8)
|
||||
```
|
||||
|
||||
## Common Utilities
|
||||
Use shared helpers from `@sim/utils` instead of writing inline implementations:
|
||||
|
||||
- `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))`
|
||||
- `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))`
|
||||
- `toError(value).message` — get error message safely. Never write `e instanceof Error ? e.message : String(e)`
|
||||
|
||||
```typescript
|
||||
// ✗ Bad
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
const msg = error instanceof Error ? error.message : String(error)
|
||||
const err = error instanceof Error ? error : new Error(String(error))
|
||||
|
||||
// ✓ Good
|
||||
import { sleep } from '@sim/utils/helpers'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
await sleep(1000)
|
||||
const msg = toError(error).message
|
||||
const err = toError(error)
|
||||
```
|
||||
|
||||
## Package Manager
|
||||
Use `bun` and `bunx`, not `npm` and `npx`.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
---
|
||||
description: Isolated-vm sandbox worker security policy. Hard rules for anything that lives in the worker child process that runs user code.
|
||||
globs: ["apps/sim/lib/execution/isolated-vm-worker.cjs", "apps/sim/lib/execution/isolated-vm.ts", "apps/sim/lib/execution/sandbox/**", "apps/sim/sandbox-tasks/**"]
|
||||
---
|
||||
|
||||
# Sim Sandbox — Worker Security Policy
|
||||
|
||||
The isolated-vm worker child process at
|
||||
`apps/sim/lib/execution/isolated-vm-worker.cjs` runs untrusted user code inside
|
||||
V8 isolates. The process itself is a trust boundary. Everything in this rule is
|
||||
about what must **never** live in that process.
|
||||
|
||||
## Hard rules
|
||||
|
||||
1. **No app credentials in the worker process**. The worker must not hold, load,
|
||||
or receive via IPC: database URLs, Redis URLs, AWS keys, Stripe keys,
|
||||
session-signing keys, encryption keys, OAuth client secrets, internal API
|
||||
secrets, or any LLM / email / search provider API keys. If you catch yourself
|
||||
`require`'ing `@/lib/auth`, `@sim/db`, `@/lib/uploads/core/storage-service`,
|
||||
or anything that imports `env` directly inside the worker, stop and use a
|
||||
host-side broker instead.
|
||||
|
||||
2. **Host-side brokers own all credentialed work**. The worker can only access
|
||||
resources through `ivm.Reference` / `ivm.Callback` bridges back to the host
|
||||
process. Today the only broker is `workspaceFileBroker`
|
||||
(`apps/sim/lib/execution/sandbox/brokers/workspace-file.ts`); adding a new
|
||||
one requires co-reviewing this file.
|
||||
|
||||
3. **Host-side brokers must scope every resource access to a single tenant**.
|
||||
The `SandboxBrokerContext` always carries `workspaceId`. Any new broker that
|
||||
accesses storage, DB, or an external API must use `ctx.workspaceId` to scope
|
||||
the lookup — never accept a raw path, key, or URL from isolate code without
|
||||
validation.
|
||||
|
||||
4. **Nothing that runs in the isolate is trusted, even if we wrote it**. The
|
||||
task `bootstrap` and `finalize` strings in `apps/sim/sandbox-tasks/` execute
|
||||
inside the isolate. They must treat `globalThis` as adversarial — no pulling
|
||||
values from it that might have been mutated by user code. The hardening
|
||||
script in `executeTask` undefines dangerous globals before user code runs.
|
||||
|
||||
## Why
|
||||
|
||||
A V8 JIT bug (Chrome ships these roughly monthly) gives an attacker a native
|
||||
code primitive inside the process that owns whatever that process can reach.
|
||||
If the worker only holds `isolated-vm` + a single narrow workspace-file broker,
|
||||
a V8 escape leaks one tenant's files. If the worker holds a Stripe key or a DB
|
||||
connection, a V8 escape leaks the service.
|
||||
|
||||
The original `doc-worker.cjs` vulnerability (CVE-class, 225 production secrets
|
||||
leaked via `/proc/1/environ`) was the forcing function for this architecture.
|
||||
Keep the blast radius small.
|
||||
|
||||
## Checklist for changes to `isolated-vm-worker.cjs`
|
||||
|
||||
Before landing any change that adds a new `require(...)` or `process.send(...)`
|
||||
payload or `ivm.Reference` wrapper in the worker:
|
||||
|
||||
- [ ] Does it load a credential, key, connection string, or secret? If yes,
|
||||
move it host-side and expose as a broker.
|
||||
- [ ] Does it import from `@/lib/auth`, `@sim/db`, `@/lib/uploads/core/*`,
|
||||
`@/lib/core/config/env`, or any module that reads `process.env` of the
|
||||
main app? If yes, same — move host-side.
|
||||
- [ ] Does it expose a resource that's workspace-scoped without taking a
|
||||
`workspaceId`? If yes, re-scope.
|
||||
- [ ] Did you update the broker limits (`IVM_MAX_BROKER_ARGS_JSON_CHARS`,
|
||||
`IVM_MAX_BROKER_RESULT_JSON_CHARS`, `IVM_MAX_BROKERS_PER_EXECUTION`) if
|
||||
the new broker can emit large payloads or fire frequently?
|
||||
|
||||
## What the worker *may* hold
|
||||
|
||||
- `isolated-vm` module
|
||||
- Node built-ins: `node:fs` (only for reading the checked-in bundle `.cjs`
|
||||
files) and `node:path`
|
||||
- The three prebuilt library bundles under
|
||||
`apps/sim/lib/execution/sandbox/bundles/*.cjs`
|
||||
- IPC message handlers for `execute`, `cancel`, `fetchResponse`,
|
||||
`brokerResponse`
|
||||
|
||||
The worker deliberately has **no host-side logger**. All errors and
|
||||
diagnostics flow through IPC back to the host, which has `@sim/logger`. Do
|
||||
not add `createLogger` or console-based logging to the worker — it would
|
||||
require pulling the main app's config / env, which is exactly what this
|
||||
rule is preventing.
|
||||
|
||||
Anything else is suspect.
|
||||
@@ -3,7 +3,6 @@ description: Testing patterns with Vitest and @sim/testing
|
||||
globs: ["apps/sim/**/*.test.ts", "apps/sim/**/*.test.tsx"]
|
||||
---
|
||||
|
||||
|
||||
# Testing Patterns
|
||||
|
||||
Use Vitest. Test files: `feature.ts` → `feature.test.ts`
|
||||
@@ -13,12 +12,8 @@ Use Vitest. Test files: `feature.ts` → `feature.test.ts`
|
||||
These modules are mocked globally — do NOT re-mock them in test files unless you need to override behavior:
|
||||
|
||||
- `@sim/db` → `databaseMock`
|
||||
- `@sim/db/schema` → `schemaMock`
|
||||
- `drizzle-orm` → `drizzleOrmMock`
|
||||
- `@sim/logger` → `loggerMock`
|
||||
- `@/lib/auth` → `authMock`
|
||||
- `@/lib/auth/hybrid` → `hybridAuthMock` (with default session-delegating behavior)
|
||||
- `@/lib/core/utils/request` → `requestUtilsMock`
|
||||
- `@/stores/console/store`, `@/stores/terminal`, `@/stores/execution/store`
|
||||
- `@/blocks/registry`
|
||||
- `@trigger.dev/sdk`
|
||||
@@ -106,6 +101,10 @@ vi.mock('@/lib/workspaces/utils', () => ({
|
||||
}))
|
||||
```
|
||||
|
||||
### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing`
|
||||
|
||||
These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead.
|
||||
|
||||
### Mock heavy transitive dependencies
|
||||
|
||||
If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them:
|
||||
@@ -135,61 +134,38 @@ await new Promise(r => setTimeout(r, 1))
|
||||
vi.useFakeTimers()
|
||||
```
|
||||
|
||||
## Centralized Mocks (prefer over local declarations)
|
||||
|
||||
`@sim/testing` exports ready-to-use mock modules for common dependencies. Import and pass directly to `vi.mock()` — no `vi.hoisted()` boilerplate needed. Each paired `*MockFns` object exposes the underlying `vi.fn()`s for per-test overrides.
|
||||
|
||||
| Module mocked | Import | Factory form |
|
||||
|---|---|---|
|
||||
| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` |
|
||||
| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` |
|
||||
| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` |
|
||||
| `@sim/audit` | `auditMock`, `auditMockFns` | `vi.mock('@sim/audit', () => auditMock)` |
|
||||
| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` |
|
||||
| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` |
|
||||
| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` |
|
||||
| `@/lib/core/config/env` | `envMock`, `createEnvMock(overrides)` | `vi.mock('@/lib/core/config/env', () => envMock)` |
|
||||
| `@/lib/core/config/feature-flags` | `featureFlagsMock` | `vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock)` |
|
||||
| `@/lib/core/config/redis` | `redisConfigMock`, `redisConfigMockFns` | `vi.mock('@/lib/core/config/redis', () => redisConfigMock)` |
|
||||
| `@/lib/core/security/encryption` | `encryptionMock`, `encryptionMockFns` | `vi.mock('@/lib/core/security/encryption', () => encryptionMock)` |
|
||||
| `@/lib/core/security/input-validation.server` | `inputValidationMock`, `inputValidationMockFns` | `vi.mock('@/lib/core/security/input-validation.server', () => inputValidationMock)` |
|
||||
| `@/lib/core/utils/request` | `requestUtilsMock`, `requestUtilsMockFns` | `vi.mock('@/lib/core/utils/request', () => requestUtilsMock)` |
|
||||
| `@/lib/core/utils/urls` | `urlsMock`, `urlsMockFns` | `vi.mock('@/lib/core/utils/urls', () => urlsMock)` |
|
||||
| `@/lib/execution/preprocessing` | `executionPreprocessingMock`, `executionPreprocessingMockFns` | `vi.mock('@/lib/execution/preprocessing', () => executionPreprocessingMock)` |
|
||||
| `@/lib/logs/execution/logging-session` | `loggingSessionMock`, `loggingSessionMockFns`, `LoggingSessionMock` | `vi.mock('@/lib/logs/execution/logging-session', () => loggingSessionMock)` |
|
||||
| `@/lib/workflows/orchestration` | `workflowsOrchestrationMock`, `workflowsOrchestrationMockFns` | `vi.mock('@/lib/workflows/orchestration', () => workflowsOrchestrationMock)` |
|
||||
| `@/lib/workflows/persistence/utils` | `workflowsPersistenceUtilsMock`, `workflowsPersistenceUtilsMockFns` | `vi.mock('@/lib/workflows/persistence/utils', () => workflowsPersistenceUtilsMock)` |
|
||||
| `@/lib/workflows/utils` | `workflowsUtilsMock`, `workflowsUtilsMockFns` | `vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock)` |
|
||||
| `@/lib/workspaces/permissions/utils` | `permissionsMock`, `permissionsMockFns` | `vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)` |
|
||||
| `@sim/db/schema` | `schemaMock` | `vi.mock('@sim/db/schema', () => schemaMock)` |
|
||||
## Mock Pattern Reference
|
||||
|
||||
### Auth mocking (API routes)
|
||||
|
||||
```typescript
|
||||
import { authMock, authMockFns } from '@sim/testing'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
const { mockGetSession } = vi.hoisted(() => ({
|
||||
mockGetSession: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/auth', () => authMock)
|
||||
vi.mock('@/lib/auth', () => ({
|
||||
auth: { api: { getSession: vi.fn() } },
|
||||
getSession: mockGetSession,
|
||||
}))
|
||||
|
||||
import { GET } from '@/app/api/my-route/route'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
|
||||
})
|
||||
// In tests:
|
||||
mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } })
|
||||
mockGetSession.mockResolvedValue(null) // unauthenticated
|
||||
```
|
||||
|
||||
Only define a local `vi.mock('@/lib/auth', ...)` if the module under test consumes exports outside the centralized shape (e.g., `auth.api.verifyOneTimeToken`, `auth.api.resetPassword`).
|
||||
|
||||
### Hybrid auth mocking
|
||||
|
||||
```typescript
|
||||
import { hybridAuthMock, hybridAuthMockFns } from '@sim/testing'
|
||||
const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({
|
||||
mockCheckSessionOrInternalAuth: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)
|
||||
vi.mock('@/lib/auth/hybrid', () => ({
|
||||
checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth,
|
||||
}))
|
||||
|
||||
// In tests:
|
||||
hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({
|
||||
mockCheckSessionOrInternalAuth.mockResolvedValue({
|
||||
success: true, userId: 'user-1', authType: 'session',
|
||||
})
|
||||
```
|
||||
@@ -220,23 +196,21 @@ Always prefer over local test data.
|
||||
|
||||
| Category | Utilities |
|
||||
|----------|-----------|
|
||||
| **Module mocks** | See "Centralized Mocks" table above |
|
||||
| **Logger helpers** | `loggerMock`, `createMockLogger()`, `getLoggerCalls()`, `clearLoggerMocks()` |
|
||||
| **Database helpers** | `databaseMock`, `drizzleOrmMock`, `createMockDb()`, `createMockSql()`, `createMockSqlOperators()` |
|
||||
| **Fetch helpers** | `setupGlobalFetchMock()`, `createMockFetch()`, `createMockResponse()`, `mockFetchError()` |
|
||||
| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` |
|
||||
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` |
|
||||
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
|
||||
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
|
||||
| **Requests** | `createMockRequest()`, `createMockFormDataRequest()` |
|
||||
| **Requests** | `createMockRequest()`, `createEnvMock()` |
|
||||
|
||||
## Rules Summary
|
||||
|
||||
1. `@vitest-environment node` unless DOM is required
|
||||
2. Prefer centralized mocks from `@sim/testing` (see table above) over local `vi.hoisted()` + `vi.mock()` boilerplate
|
||||
3. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
|
||||
4. `vi.mock()` calls before importing mocked modules
|
||||
2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
|
||||
3. `vi.mock()` calls before importing mocked modules
|
||||
4. `@sim/testing` utilities over local mocks
|
||||
5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach`
|
||||
6. No `vi.importActual()` — mock everything explicitly
|
||||
7. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
|
||||
8. Use absolute imports in test files
|
||||
9. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`
|
||||
7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks
|
||||
8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
|
||||
9. Use absolute imports in test files
|
||||
10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM oven/bun:1.3.13-alpine
|
||||
FROM oven/bun:1.3.11-alpine
|
||||
|
||||
# Install necessary packages for development
|
||||
RUN apk add --no-cache \
|
||||
|
||||
@@ -71,7 +71,7 @@ fi
|
||||
|
||||
# Set up environment variables if .env doesn't exist for the sim app
|
||||
if [ ! -f "apps/sim/.env" ]; then
|
||||
echo "📄 Creating apps/sim/.env from template..."
|
||||
echo "📄 Creating .env file from template..."
|
||||
if [ -f "apps/sim/.env.example" ]; then
|
||||
cp apps/sim/.env.example apps/sim/.env
|
||||
else
|
||||
@@ -79,18 +79,6 @@ if [ ! -f "apps/sim/.env" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set up env for the realtime server (must match the shared values in apps/sim/.env)
|
||||
if [ ! -f "apps/realtime/.env" ] && [ -f "apps/realtime/.env.example" ]; then
|
||||
echo "📄 Creating apps/realtime/.env from template..."
|
||||
cp apps/realtime/.env.example apps/realtime/.env
|
||||
fi
|
||||
|
||||
# Set up packages/db/.env for drizzle-kit and migration scripts
|
||||
if [ ! -f "packages/db/.env" ] && [ -f "packages/db/.env.example" ]; then
|
||||
echo "📄 Creating packages/db/.env from template..."
|
||||
cp packages/db/.env.example packages/db/.env
|
||||
fi
|
||||
|
||||
# Generate schema and run database migrations
|
||||
echo "🗃️ Running database schema generation and migrations..."
|
||||
echo "Generating schema..."
|
||||
|
||||
259
.github/CONTRIBUTING.md
vendored
@@ -2,15 +2,8 @@
|
||||
|
||||
Thank you for your interest in contributing to Sim! Our goal is to provide developers with a powerful, user-friendly platform for building, testing, and optimizing agentic workflows. We welcome contributions in all forms—from bug fixes and design improvements to brand-new features.
|
||||
|
||||
> **Project Overview:**
|
||||
> Sim is a Turborepo monorepo with two deployable apps and a set of shared packages:
|
||||
>
|
||||
> - `apps/sim/` — the main Next.js application (App Router, ReactFlow, Zustand, Shadcn, Tailwind CSS).
|
||||
> - `apps/realtime/` — a small Bun + Socket.IO server that powers the collaborative canvas. Shares DB and Better Auth secrets with `apps/sim` via `@sim/*` packages.
|
||||
> - `apps/docs/` — Fumadocs-based documentation site.
|
||||
> - `packages/` — shared workspace packages (`@sim/db`, `@sim/auth`, `@sim/audit`, `@sim/workflow-types`, `@sim/workflow-persistence`, `@sim/workflow-authz`, `@sim/realtime-protocol`, `@sim/security`, `@sim/logger`, `@sim/utils`, `@sim/testing`, `@sim/tsconfig`).
|
||||
>
|
||||
> Strict one-way dependency flow: `apps/* → packages/*`. Packages never import from apps. Please ensure your contributions follow this and our best practices for clarity, maintainability, and consistency.
|
||||
> **Project Overview:**
|
||||
> Sim is a monorepo using Turborepo, containing the main application (`apps/sim/`), documentation (`apps/docs/`), and shared packages (`packages/`). The main application is built with Next.js (app router), ReactFlow, Zustand, Shadcn, and Tailwind CSS. Please ensure your contributions follow our best practices for clarity, maintainability, and consistency.
|
||||
|
||||
---
|
||||
|
||||
@@ -31,17 +24,14 @@ Thank you for your interest in contributing to Sim! Our goal is to provide devel
|
||||
|
||||
We strive to keep our workflow as simple as possible. To contribute:
|
||||
|
||||
1. **Fork the Repository**
|
||||
1. **Fork the Repository**
|
||||
Click the **Fork** button on GitHub to create your own copy of the project.
|
||||
|
||||
2. **Clone Your Fork**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/sim.git
|
||||
cd sim
|
||||
```
|
||||
|
||||
3. **Create a Feature Branch**
|
||||
3. **Create a Feature Branch**
|
||||
Create a new branch with a descriptive name:
|
||||
|
||||
```bash
|
||||
@@ -50,23 +40,21 @@ We strive to keep our workflow as simple as possible. To contribute:
|
||||
|
||||
Use a clear naming convention to indicate the type of work (e.g., `feat/`, `fix/`, `docs/`).
|
||||
|
||||
4. **Make Your Changes**
|
||||
4. **Make Your Changes**
|
||||
Ensure your changes are small, focused, and adhere to our coding guidelines.
|
||||
|
||||
5. **Commit Your Changes**
|
||||
5. **Commit Your Changes**
|
||||
Write clear, descriptive commit messages that follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#specification) specification. This allows us to maintain a coherent project history and generate changelogs automatically. For example:
|
||||
|
||||
- `feat(api): add new endpoint for user authentication`
|
||||
- `fix(ui): resolve button alignment issue`
|
||||
- `docs: update contribution guidelines`
|
||||
|
||||
6. **Push Your Branch**
|
||||
|
||||
```bash
|
||||
git push origin feat/your-feature-name
|
||||
```
|
||||
|
||||
7. **Create a Pull Request**
|
||||
7. **Create a Pull Request**
|
||||
Open a pull request against the `staging` branch on GitHub. Please provide a clear description of the changes and reference any relevant issues (e.g., `fixes #123`).
|
||||
|
||||
---
|
||||
@@ -77,7 +65,7 @@ If you discover a bug or have a feature request, please open an issue in our Git
|
||||
|
||||
- Provide a clear, descriptive title.
|
||||
- Include as many details as possible (steps to reproduce, screenshots, etc.).
|
||||
- **Tag Your Issue Appropriately:**
|
||||
- **Tag Your Issue Appropriately:**
|
||||
Use the following labels to help us categorize your issue:
|
||||
- **active:** Actively working on it right now.
|
||||
- **bug:** Something isn't working.
|
||||
@@ -94,11 +82,12 @@ If you discover a bug or have a feature request, please open an issue in our Git
|
||||
|
||||
Before creating a pull request:
|
||||
|
||||
- **Ensure Your Branch Is Up-to-Date:**
|
||||
- **Ensure Your Branch Is Up-to-Date:**
|
||||
Rebase your branch onto the latest `staging` branch to prevent merge conflicts.
|
||||
- **Follow the Guidelines:**
|
||||
- **Follow the Guidelines:**
|
||||
Make sure your changes are well-tested, follow our coding standards, and include relevant documentation if necessary.
|
||||
- **Reference Issues:**
|
||||
|
||||
- **Reference Issues:**
|
||||
If your PR addresses an existing issue, include `refs #<issue-number>` or `fixes #<issue-number>` in your PR description.
|
||||
|
||||
Our maintainers will review your pull request and provide feedback. We aim to make the review process as smooth and timely as possible.
|
||||
@@ -177,27 +166,27 @@ To use local models with Sim:
|
||||
|
||||
1. Install Ollama and pull models:
|
||||
|
||||
```bash
|
||||
# Install Ollama (if not already installed)
|
||||
curl -fsSL https://ollama.ai/install.sh | sh
|
||||
```bash
|
||||
# Install Ollama (if not already installed)
|
||||
curl -fsSL https://ollama.ai/install.sh | sh
|
||||
|
||||
# Pull a model (e.g., gemma3:4b)
|
||||
ollama pull gemma3:4b
|
||||
```
|
||||
# Pull a model (e.g., gemma3:4b)
|
||||
ollama pull gemma3:4b
|
||||
```
|
||||
|
||||
2. Start Sim with local model support:
|
||||
|
||||
```bash
|
||||
# With NVIDIA GPU support
|
||||
docker compose --profile local-gpu -f docker-compose.ollama.yml up -d
|
||||
```bash
|
||||
# With NVIDIA GPU support
|
||||
docker compose --profile local-gpu -f docker-compose.ollama.yml up -d
|
||||
|
||||
# Without GPU (CPU only)
|
||||
docker compose --profile local-cpu -f docker-compose.ollama.yml up -d
|
||||
# Without GPU (CPU only)
|
||||
docker compose --profile local-cpu -f docker-compose.ollama.yml up -d
|
||||
|
||||
# If hosting on a server, update the environment variables in the docker-compose.prod.yml file
|
||||
# to include the server's public IP then start again (OLLAMA_URL to i.e. http://1.1.1.1:11434)
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
# If hosting on a server, update the environment variables in the docker-compose.prod.yml file
|
||||
# to include the server's public IP then start again (OLLAMA_URL to i.e. http://1.1.1.1:11434)
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
### Option 3: Using VS Code / Cursor Dev Containers
|
||||
|
||||
@@ -212,104 +201,61 @@ Dev Containers provide a consistent and easy-to-use development environment:
|
||||
2. **Setup Steps:**
|
||||
|
||||
- Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/sim.git
|
||||
cd sim
|
||||
```
|
||||
|
||||
- Open the project in VS Code/Cursor.
|
||||
- When prompted, click "Reopen in Container" (or press F1 and select "Remote-Containers: Reopen in Container").
|
||||
- Wait for the container to build and initialize.
|
||||
- Open the project in VS Code/Cursor
|
||||
- When prompted, click "Reopen in Container" (or press F1 and select "Remote-Containers: Reopen in Container")
|
||||
- Wait for the container to build and initialize
|
||||
|
||||
3. **Start Developing:**
|
||||
|
||||
- Run `bun run dev:full` in the terminal or use the `sim-start` alias.
|
||||
- This starts both the main application and the realtime socket server.
|
||||
- All dependencies and configurations are automatically set up.
|
||||
- Your changes will be automatically hot-reloaded.
|
||||
- Run `bun run dev:full` in the terminal or use the `sim-start` alias
|
||||
- This starts both the main application and the realtime socket server
|
||||
- All dependencies and configurations are automatically set up
|
||||
- Your changes will be automatically hot-reloaded
|
||||
|
||||
4. **GitHub Codespaces:**
|
||||
|
||||
- This setup also works with GitHub Codespaces if you prefer development in the browser.
|
||||
- Just click "Code" → "Codespaces" → "Create codespace on staging".
|
||||
- This setup also works with GitHub Codespaces if you prefer development in the browser
|
||||
- Just click "Code" → "Codespaces" → "Create codespace on staging"
|
||||
|
||||
### Option 4: Manual Setup
|
||||
|
||||
If you prefer not to use Docker or Dev Containers. **All commands run from the repository root unless explicitly noted.**
|
||||
|
||||
1. **Clone and Install:**
|
||||
If you prefer not to use Docker or Dev Containers:
|
||||
|
||||
1. **Clone the Repository:**
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/sim.git
|
||||
cd sim
|
||||
bun install
|
||||
```
|
||||
|
||||
Bun workspaces handle dependency resolution for all apps and packages from the root `bun install`.
|
||||
2. **Set Up Environment:**
|
||||
|
||||
2. **Set Up Environment Files:**
|
||||
- Navigate to the app directory:
|
||||
```bash
|
||||
cd apps/sim
|
||||
```
|
||||
- Copy `.env.example` to `.env`
|
||||
- Configure required variables (DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL)
|
||||
|
||||
We use **per-app `.env` files** (the Turborepo-canonical pattern), not a single root `.env`. Three files are needed for local dev:
|
||||
3. **Set Up Database:**
|
||||
|
||||
```bash
|
||||
# Main app — large, app-specific (OAuth secrets, LLM keys, Stripe, etc.)
|
||||
cp apps/sim/.env.example apps/sim/.env
|
||||
|
||||
# Realtime server — small, only the values shared with the main app
|
||||
cp apps/realtime/.env.example apps/realtime/.env
|
||||
|
||||
# DB tooling (drizzle-kit, db:migrate)
|
||||
cp packages/db/.env.example packages/db/.env
|
||||
bunx drizzle-kit push
|
||||
```
|
||||
|
||||
At minimum, each `.env` needs `DATABASE_URL`. `apps/sim/.env` and `apps/realtime/.env` additionally need matching values for `BETTER_AUTH_URL`, `BETTER_AUTH_SECRET`, `INTERNAL_API_SECRET`, and `NEXT_PUBLIC_APP_URL`. `apps/sim/.env` also needs `ENCRYPTION_KEY` and `API_ENCRYPTION_KEY`. Generate any 32-char secrets with `openssl rand -hex 32`.
|
||||
|
||||
The same `BETTER_AUTH_SECRET`, `INTERNAL_API_SECRET`, and `DATABASE_URL` must appear in both `apps/sim/.env` and `apps/realtime/.env` so the two services share auth and DB. After editing `apps/sim/.env`, you can mirror the shared subset into the realtime env in one shot:
|
||||
|
||||
```bash
|
||||
grep -E '^(DATABASE_URL|BETTER_AUTH_URL|BETTER_AUTH_SECRET|INTERNAL_API_SECRET|NEXT_PUBLIC_APP_URL|REDIS_URL)=' apps/sim/.env > apps/realtime/.env
|
||||
grep -E '^DATABASE_URL=' apps/sim/.env > packages/db/.env
|
||||
```
|
||||
|
||||
3. **Run Database Migrations:**
|
||||
|
||||
Migrations live in `packages/db/migrations/`. Run them via the dedicated workspace script:
|
||||
|
||||
```bash
|
||||
cd packages/db && bun run db:migrate && cd ../..
|
||||
```
|
||||
|
||||
For ad-hoc schema iteration during development you can also use `bun run db:push` from `packages/db`, but `db:migrate` is the canonical command for both local and CI/CD setups.
|
||||
|
||||
4. **Run the Development Servers:**
|
||||
4. **Run the Development Server:**
|
||||
|
||||
```bash
|
||||
bun run dev:full
|
||||
```
|
||||
|
||||
This launches both apps with coloured prefixes:
|
||||
|
||||
- `[App]` — Next.js on `http://localhost:3000`
|
||||
- `[Realtime]` — Socket.IO on `http://localhost:3002`
|
||||
|
||||
Or run them separately:
|
||||
|
||||
```bash
|
||||
bun run dev # Next.js app only
|
||||
bun run dev:sockets # realtime server only
|
||||
```
|
||||
This command starts both the main application and the realtime socket server required for full functionality.
|
||||
|
||||
5. **Make Your Changes and Test Locally.**
|
||||
|
||||
Before opening a PR, run the same checks CI runs:
|
||||
|
||||
```bash
|
||||
bun run type-check # TypeScript across every workspace
|
||||
bun run lint:check # Biome lint across every workspace
|
||||
bun run test # Vitest across every workspace
|
||||
```
|
||||
|
||||
### Email Template Development
|
||||
|
||||
When working on email templates, you can preview them using a local email preview server:
|
||||
@@ -317,19 +263,18 @@ When working on email templates, you can preview them using a local email previe
|
||||
1. **Run the Email Preview Server:**
|
||||
|
||||
```bash
|
||||
cd apps/sim && bun run email:dev
|
||||
bun run email:dev
|
||||
```
|
||||
|
||||
2. **Access the Preview:**
|
||||
|
||||
- Open `http://localhost:3000` in your browser.
|
||||
- You'll see a list of all email templates.
|
||||
- Click on any template to view and test it with various parameters.
|
||||
- Open `http://localhost:3000` in your browser
|
||||
- You'll see a list of all email templates
|
||||
- Click on any template to view and test it with various parameters
|
||||
|
||||
3. **Templates Location:**
|
||||
|
||||
- Email templates live in `apps/sim/components/emails/`.
|
||||
- Changes hot-reload automatically in the preview.
|
||||
- Email templates are located in `sim/app/emails/`
|
||||
- After making changes to templates, they will automatically update in the preview
|
||||
|
||||
---
|
||||
|
||||
@@ -337,41 +282,28 @@ When working on email templates, you can preview them using a local email previe
|
||||
|
||||
Sim is built in a modular fashion where blocks and tools extend the platform's functionality. To maintain consistency and quality, please follow the guidelines below when adding a new block or tool.
|
||||
|
||||
> **Use the skill guides for step-by-step recipes.** The repository ships opinionated, end-to-end guides under `.agents/skills/` that cover the exact file layout, conventions, registry wiring, and gotchas for each kind of contribution. Read the relevant SKILL.md before you start writing code:
|
||||
>
|
||||
> | Adding… | Read |
|
||||
> | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
> | A new integration end-to-end (tools + block + icon + optional triggers + all registrations) | [`.agents/skills/add-integration/SKILL.md`](../.agents/skills/add-integration/SKILL.md) |
|
||||
> | Just a block (or aligning an existing block with its tools) | [`.agents/skills/add-block/SKILL.md`](../.agents/skills/add-block/SKILL.md) |
|
||||
> | Just tool configs for a service | [`.agents/skills/add-tools/SKILL.md`](../.agents/skills/add-tools/SKILL.md) |
|
||||
> | A webhook trigger for a service | [`.agents/skills/add-trigger/SKILL.md`](../.agents/skills/add-trigger/SKILL.md) |
|
||||
> | A knowledge-base connector (sync docs from an external source) | [`.agents/skills/add-connector/SKILL.md`](../.agents/skills/add-connector/SKILL.md) |
|
||||
>
|
||||
> The shorter overview below is a high-level reference; the SKILL.md files are the authoritative source of truth and stay in sync with the codebase.
|
||||
|
||||
### Where to Add Your Code
|
||||
|
||||
- **Blocks:** Create your new block file under the `apps/sim/blocks/blocks/` directory. The name of the file should match the provider name (e.g., `pinecone.ts`).
|
||||
- **Tools:** Create a new directory under `apps/sim/tools/` with the same name as the provider (e.g., `apps/sim/tools/pinecone`).
|
||||
- **Blocks:** Create your new block file under the `/apps/sim/blocks/blocks` directory. The name of the file should match the provider name (e.g., `pinecone.ts`).
|
||||
- **Tools:** Create a new directory under `/apps/sim/tools` with the same name as the provider (e.g., `/apps/sim/tools/pinecone`).
|
||||
|
||||
In addition, you will need to update the registries:
|
||||
|
||||
- **Block Registry:** Add your block to `apps/sim/blocks/registry.ts`. (`apps/sim/blocks/index.ts` re-exports lookups from the registry; you do not need to edit it.)
|
||||
- **Tool Registry:** Add your tool to `apps/sim/tools/index.ts`.
|
||||
- **Block Registry:** Update the blocks index (`/apps/sim/blocks/index.ts`) to include your new block.
|
||||
- **Tool Registry:** Update the tools registry (`/apps/sim/tools/index.ts`) to add your new tool.
|
||||
|
||||
### How to Create a New Block
|
||||
|
||||
1. **Create a New File:**
|
||||
Create a file for your block named after the provider (e.g., `pinecone.ts`) in the `apps/sim/blocks/blocks/` directory.
|
||||
1. **Create a New File:**
|
||||
Create a file for your block named after the provider (e.g., `pinecone.ts`) in the `/apps/sim/blocks/blocks` directory.
|
||||
|
||||
2. **Create a New Icon:**
|
||||
Create a new icon for your block in `apps/sim/components/icons.tsx`. The icon should follow the same naming convention as the block (e.g., `PineconeIcon`).
|
||||
Create a new icon for your block in the `/apps/sim/components/icons.tsx` file. The icon should follow the same naming convention as the block (e.g., `PineconeIcon`).
|
||||
|
||||
3. **Define the Block Configuration:**
|
||||
3. **Define the Block Configuration:**
|
||||
Your block should export a constant of type `BlockConfig`. For example:
|
||||
|
||||
```typescript
|
||||
// apps/sim/blocks/blocks/pinecone.ts
|
||||
```typescript:/apps/sim/blocks/blocks/pinecone.ts
|
||||
import { PineconeIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { PineconeResponse } from '@/tools/pinecone/types'
|
||||
@@ -389,7 +321,7 @@ In addition, you will need to update the registries:
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
type: 'dropdown'
|
||||
required: true,
|
||||
options: [
|
||||
{ label: 'Generate Embeddings', id: 'generate' },
|
||||
@@ -400,7 +332,7 @@ In addition, you will need to update the registries:
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
type: 'short-input'
|
||||
placeholder: 'Your Pinecone API key',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -438,11 +370,10 @@ In addition, you will need to update the registries:
|
||||
}
|
||||
```
|
||||
|
||||
4. **Register Your Block:**
|
||||
Add your block to the blocks registry (`apps/sim/blocks/registry.ts`):
|
||||
4. **Register Your Block:**
|
||||
Add your block to the blocks registry (`/apps/sim/blocks/registry.ts`):
|
||||
|
||||
```typescript
|
||||
// apps/sim/blocks/registry.ts
|
||||
```typescript:/apps/sim/blocks/registry.ts
|
||||
import { PineconeBlock } from '@/blocks/blocks/pinecone'
|
||||
|
||||
// Registry of all available blocks
|
||||
@@ -454,25 +385,24 @@ In addition, you will need to update the registries:
|
||||
|
||||
The block will be automatically available to the application through the registry.
|
||||
|
||||
5. **Test Your Block:**
|
||||
5. **Test Your Block:**
|
||||
Ensure that the block displays correctly in the UI and that its functionality works as expected.
|
||||
|
||||
### How to Create a New Tool
|
||||
|
||||
1. **Create a New Directory:**
|
||||
Create a directory under `apps/sim/tools/` with the same name as the provider (e.g., `apps/sim/tools/pinecone`).
|
||||
1. **Create a New Directory:**
|
||||
Create a directory under `/apps/sim/tools` with the same name as the provider (e.g., `/apps/sim/tools/pinecone`).
|
||||
|
||||
2. **Create Tool Files:**
|
||||
2. **Create Tool Files:**
|
||||
Create separate files for each tool functionality with descriptive names (e.g., `fetch.ts`, `generate_embeddings.ts`, `search_text.ts`) in your tool directory.
|
||||
|
||||
3. **Create a Types File:**
|
||||
3. **Create a Types File:**
|
||||
Create a `types.ts` file in your tool directory to define and export all types related to your tools.
|
||||
|
||||
4. **Create an Index File:**
|
||||
4. **Create an Index File:**
|
||||
Create an `index.ts` file in your tool directory that imports and exports all tools:
|
||||
|
||||
```typescript
|
||||
// apps/sim/tools/pinecone/index.ts
|
||||
```typescript:/apps/sim/tools/pinecone/index.ts
|
||||
import { fetchTool } from './fetch'
|
||||
import { generateEmbeddingsTool } from './generate_embeddings'
|
||||
import { searchTextTool } from './search_text'
|
||||
@@ -480,11 +410,10 @@ In addition, you will need to update the registries:
|
||||
export { fetchTool, generateEmbeddingsTool, searchTextTool }
|
||||
```
|
||||
|
||||
5. **Define the Tool Configuration:**
|
||||
5. **Define the Tool Configuration:**
|
||||
Your tool should export a constant with a naming convention of `{toolName}Tool`. The tool ID should follow the format `{provider}_{tool_name}`. For example:
|
||||
|
||||
```typescript
|
||||
// apps/sim/tools/pinecone/fetch.ts
|
||||
```typescript:/apps/sim/tools/pinecone/fetch.ts
|
||||
import { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import { PineconeParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
|
||||
@@ -520,12 +449,11 @@ In addition, you will need to update the registries:
|
||||
}
|
||||
```
|
||||
|
||||
6. **Register Your Tool:**
|
||||
Update the tools registry in `apps/sim/tools/index.ts` to include your new tool:
|
||||
6. **Register Your Tool:**
|
||||
Update the tools registry in `/apps/sim/tools/index.ts` to include your new tool:
|
||||
|
||||
```typescript
|
||||
// apps/sim/tools/index.ts
|
||||
import { fetchTool, generateEmbeddingsTool, searchTextTool } from '@/tools/pinecone'
|
||||
```typescript:/apps/sim/tools/index.ts
|
||||
import { fetchTool, generateEmbeddingsTool, searchTextTool } from '/@tools/pinecone'
|
||||
// ... other imports
|
||||
|
||||
export const tools: Record<string, ToolConfig> = {
|
||||
@@ -536,14 +464,13 @@ In addition, you will need to update the registries:
|
||||
}
|
||||
```
|
||||
|
||||
7. **Test Your Tool:**
|
||||
7. **Test Your Tool:**
|
||||
Ensure that your tool functions correctly by making test requests and verifying the responses.
|
||||
|
||||
8. **Generate Documentation:**
|
||||
Run the documentation generator (from `apps/sim`) to create docs for your new tool:
|
||||
|
||||
8. **Generate Documentation:**
|
||||
Run the documentation generator to create docs for your new tool:
|
||||
```bash
|
||||
cd apps/sim && bun run generate-docs
|
||||
./scripts/generate-docs.sh
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
@@ -553,7 +480,7 @@ Maintaining consistent naming across the codebase is critical for auto-generatio
|
||||
- **Block Files:** Name should match the provider (e.g., `pinecone.ts`)
|
||||
- **Block Export:** Should be named `{Provider}Block` (e.g., `PineconeBlock`)
|
||||
- **Icons:** Should be named `{Provider}Icon` (e.g., `PineconeIcon`)
|
||||
- **Tool Directories:** Should match the provider name (e.g., `tools/pinecone/`)
|
||||
- **Tool Directories:** Should match the provider name (e.g., `/tools/pinecone/`)
|
||||
- **Tool Files:** Should be named after their function (e.g., `fetch.ts`, `search_text.ts`)
|
||||
- **Tool Exports:** Should be named `{toolName}Tool` (e.g., `fetchTool`)
|
||||
- **Tool IDs:** Should follow the format `{provider}_{tool_name}` (e.g., `pinecone_fetch`)
|
||||
@@ -562,12 +489,12 @@ Maintaining consistent naming across the codebase is critical for auto-generatio
|
||||
|
||||
Sim implements a sophisticated parameter visibility system that controls how parameters are exposed to users and LLMs in agent workflows. Each parameter can have one of four visibility levels:
|
||||
|
||||
| Visibility | User Sees | LLM Sees | How It Gets Set |
|
||||
| ------------- | --------- | -------- | ------------------------------ |
|
||||
| `user-only` | ✅ Yes | ❌ No | User provides in UI |
|
||||
| `user-or-llm` | ✅ Yes | ✅ Yes | User provides OR LLM generates |
|
||||
| `llm-only` | ❌ No | ✅ Yes | LLM generates only |
|
||||
| `hidden` | ❌ No | ❌ No | Application injects at runtime |
|
||||
| Visibility | User Sees | LLM Sees | How It Gets Set |
|
||||
|-------------|-----------|----------|--------------------------------|
|
||||
| `user-only` | ✅ Yes | ❌ No | User provides in UI |
|
||||
| `user-or-llm` | ✅ Yes | ✅ Yes | User provides OR LLM generates |
|
||||
| `llm-only` | ❌ No | ✅ Yes | LLM generates only |
|
||||
| `hidden` | ❌ No | ❌ No | Application injects at runtime |
|
||||
|
||||
#### Visibility Guidelines
|
||||
|
||||
|
||||
2
.github/workflows/docs-embeddings.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
4
.github/workflows/i18n.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Cache Bun dependencies
|
||||
uses: actions/cache@v4
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Cache Bun dependencies
|
||||
uses: actions/cache@v4
|
||||
|
||||
2
.github/workflows/migrations.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Cache Bun dependencies
|
||||
uses: actions/cache@v4
|
||||
|
||||
2
.github/workflows/publish-cli.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Setup Node.js for npm publishing
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
2
.github/workflows/publish-ts-sdk.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Setup Node.js for npm publishing
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
11
.github/workflows/test-build.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.13
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
@@ -103,15 +103,6 @@ jobs:
|
||||
- name: Lint code
|
||||
run: bun run lint:check
|
||||
|
||||
- name: Enforce monorepo boundaries
|
||||
run: bun run check:boundaries
|
||||
|
||||
- name: Verify realtime prune graph
|
||||
run: bun run check:realtime-prune
|
||||
|
||||
- name: Type-check realtime server
|
||||
run: bunx turbo run type-check --filter=@sim/realtime
|
||||
|
||||
- name: Run tests with coverage
|
||||
env:
|
||||
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
|
||||
|
||||
47
AGENTS.md
@@ -7,7 +7,7 @@ You are a professional software engineer. All code must follow best practices: a
|
||||
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`
|
||||
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
|
||||
- **Styling**: Never update global styles. Keep all styling local to components
|
||||
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@sim/utils/id`
|
||||
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid`
|
||||
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
|
||||
|
||||
## Architecture
|
||||
@@ -20,42 +20,19 @@ You are a professional software engineer. All code must follow best practices: a
|
||||
|
||||
### Root Structure
|
||||
```
|
||||
apps/
|
||||
├── sim/ # Next.js app (UI + API routes + workflow editor)
|
||||
│ ├── app/ # Next.js app router (pages, API routes)
|
||||
│ ├── blocks/ # Block definitions and registry
|
||||
│ ├── components/ # Shared UI (emcn/, ui/)
|
||||
│ ├── executor/ # Workflow execution engine
|
||||
│ ├── hooks/ # Shared hooks (queries/, selectors/)
|
||||
│ ├── lib/ # App-wide utilities
|
||||
│ ├── providers/ # LLM provider integrations
|
||||
│ ├── stores/ # Zustand stores
|
||||
│ ├── tools/ # Tool definitions
|
||||
│ └── triggers/ # Trigger definitions
|
||||
└── realtime/ # Bun Socket.IO server (collaborative canvas)
|
||||
└── src/ # auth, config, database, handlers, middleware,
|
||||
# rooms, routes, internal/webhook-cleanup.ts
|
||||
|
||||
packages/
|
||||
├── audit/ # @sim/audit — recordAudit + AuditAction + AuditResourceType
|
||||
├── auth/ # @sim/auth — @sim/auth/verify (shared Better Auth verifier)
|
||||
├── db/ # @sim/db — drizzle schema + client
|
||||
├── logger/ # @sim/logger
|
||||
├── realtime-protocol/ # @sim/realtime-protocol — socket operation constants + zod schemas
|
||||
├── security/ # @sim/security — safeCompare
|
||||
├── tsconfig/ # shared tsconfig presets
|
||||
├── utils/ # @sim/utils
|
||||
├── workflow-authz/ # @sim/workflow-authz — authorizeWorkflowByWorkspacePermission
|
||||
├── workflow-persistence/ # @sim/workflow-persistence — raw load/save + subflow helpers
|
||||
└── workflow-types/ # @sim/workflow-types — pure BlockState/Loop/Parallel/... types
|
||||
apps/sim/
|
||||
├── app/ # Next.js app router (pages, API routes)
|
||||
├── blocks/ # Block definitions and registry
|
||||
├── components/ # Shared UI (emcn/, ui/)
|
||||
├── executor/ # Workflow execution engine
|
||||
├── hooks/ # Shared hooks (queries/, selectors/)
|
||||
├── lib/ # App-wide utilities
|
||||
├── providers/ # LLM provider integrations
|
||||
├── stores/ # Zustand stores
|
||||
├── tools/ # Tool definitions
|
||||
└── triggers/ # Trigger definitions
|
||||
```
|
||||
|
||||
### Package boundaries
|
||||
- `apps/* → packages/*` only. Packages never import from `apps/*`.
|
||||
- Each package has explicit subpath `exports` maps; no barrels that accidentally pull in heavy halves.
|
||||
- `apps/realtime` intentionally avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.
|
||||
- Auth is shared across services via the Better Auth "Shared Database Session" pattern: both apps read the same `BETTER_AUTH_SECRET` and point at the same DB via `@sim/db`.
|
||||
|
||||
### Naming Conventions
|
||||
- Components: PascalCase (`WorkflowList`)
|
||||
- Hooks: `use` prefix (`useWorkflowOperations`)
|
||||
|
||||
41
CLAUDE.md
@@ -4,12 +4,10 @@ You are a professional software engineer. All code must follow best practices: a
|
||||
|
||||
## Global Standards
|
||||
|
||||
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`. Inside API routes wrapped with `withRouteHandler`, loggers automatically include the request ID — no manual `withMetadata({ requestId })` needed
|
||||
- **API Route Handlers**: All API route handlers (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) must be wrapped with `withRouteHandler` from `@/lib/core/utils/with-route-handler`. This provides request ID tracking, automatic error logging for 4xx/5xx responses, and unhandled error catching. See "API Route Pattern" section below
|
||||
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`
|
||||
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
|
||||
- **Styling**: Never update global styles. Keep all styling local to components
|
||||
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@sim/utils/id`
|
||||
- **Common Utilities**: Use shared helpers from `@sim/utils` instead of inline implementations. `sleep(ms)` from `@sim/utils/helpers` for delays, `toError(e)` from `@sim/utils/errors` to normalize caught values.
|
||||
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid`
|
||||
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
|
||||
|
||||
## Architecture
|
||||
@@ -94,41 +92,6 @@ export function Component({ requiredProp, optionalProp = false }: ComponentProps
|
||||
|
||||
Extract when: 50+ lines, used in 2+ files, or has own state/logic. Keep inline when: < 10 lines, single use, purely presentational.
|
||||
|
||||
## API Route Pattern
|
||||
|
||||
Every API route handler must be wrapped with `withRouteHandler`. This sets up `AsyncLocalStorage`-based request context so all loggers in the request lifecycle automatically include the request ID.
|
||||
|
||||
```typescript
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
const logger = createLogger('MyAPI')
|
||||
|
||||
// Simple route
|
||||
export const GET = withRouteHandler(async (request: NextRequest) => {
|
||||
logger.info('Handling request') // automatically includes {requestId=...}
|
||||
return NextResponse.json({ ok: true })
|
||||
})
|
||||
|
||||
// Route with params
|
||||
export const DELETE = withRouteHandler(async (
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) => {
|
||||
const { id } = await params
|
||||
return NextResponse.json({ deleted: id })
|
||||
})
|
||||
|
||||
// Composing with other middleware (withRouteHandler wraps the outermost layer)
|
||||
export const POST = withRouteHandler(withAdminAuth(async (request) => {
|
||||
return NextResponse.json({ ok: true })
|
||||
}))
|
||||
```
|
||||
|
||||
Never export a bare `async function GET/POST/...` — always use `export const METHOD = withRouteHandler(...)`.
|
||||
|
||||
## Hooks
|
||||
|
||||
```typescript
|
||||
|
||||
@@ -142,15 +142,13 @@ See the [environment variables reference](https://docs.sim.ai/self-hosting/envir
|
||||
- **Database**: PostgreSQL with [Drizzle ORM](https://orm.drizzle.team)
|
||||
- **Authentication**: [Better Auth](https://better-auth.com)
|
||||
- **UI**: [Shadcn](https://ui.shadcn.com/), [Tailwind CSS](https://tailwindcss.com)
|
||||
- **Streaming Markdown**: [Streamdown](https://github.com/vercel/streamdown)
|
||||
- **State Management**: [Zustand](https://zustand-demo.pmnd.rs/), [TanStack Query](https://tanstack.com/query)
|
||||
- **State Management**: [Zustand](https://zustand-demo.pmnd.rs/)
|
||||
- **Flow Editor**: [ReactFlow](https://reactflow.dev/)
|
||||
- **Docs**: [Fumadocs](https://fumadocs.vercel.app/)
|
||||
- **Monorepo**: [Turborepo](https://turborepo.org/)
|
||||
- **Realtime**: [Socket.io](https://socket.io/)
|
||||
- **Background Jobs**: [Trigger.dev](https://trigger.dev/)
|
||||
- **Remote Code Execution**: [E2B](https://www.e2b.dev/)
|
||||
- **Isolated Code Execution**: [isolated-vm](https://github.com/laverdet/isolated-vm)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -28,36 +28,6 @@ export function AgentMailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function AgentPhoneIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 150 150' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
stroke='#007F3F'
|
||||
strokeWidth='0.15'
|
||||
strokeMiterlimit='10'
|
||||
d='m139.6 53.3c-1.4-2.3-4.9-3.3-7.6-4.8-2.7-1.3-4.2-2.4-5.7-3.6-1.9-1-2.5-2.7-3.3-3.2s-2.7-1.4-4.5 1.3c-2 2.7-4.5 6.6-6.6 11.1-2.3 5.4-6.3 14.9-6.3 18.9 0.5 4.9 3.1 4.6 6.1 7.2 2.5 2.1 2.8 5.8 1.5 12.5-1.3 6.6-4 12.8-7.8 19.2-3.3 5.1-5.8 8.7-10 9.1-5.3 0.5-12.5-3.1-16.8-5.6-1-0.6-2.5-0.9-3.8-0.2-1.3 0.5-2.2 1.6-3.2 3.3-1.5 2.5-4.6 7.7-5.8 12.2-0.5 3 0 6.4 2.9 9 1.4 1.2 2.8 2.5 4.4 3.4 5 2.8 9.6 4.5 16.5 4.9 5.3 0.2 9.3-1 13.4-3.1 2.4-1.3 6.6-4.2 9.6-7.3l1.1-1.2c2.8-3.1 8.8-10 11.6-14.5 2.3-3.5 4.8-7.4 6.9-12.3 2.9-6.7 4.4-14 5-17.9 1.2-7 2.4-17.5 3.4-31.1 0.1-4.3-0.3-6.1-1-7.3zm-4.5 6.7c-0.5 9.5-1.9 23.3-3.1 30.1-0.9 4.5-2.4 9.6-3.8 13.4-1.1 2.6-3.1 7-5.6 10.8-3.4 5.3-8.4 11.6-12 15.8-6.4 6.6-10.2 9.6-14.2 10.8-2.2 0.9-3.8 1.2-7 1.2-3.4-0.1-8-0.7-11.3-2.2-3-1.2-7-4-6.9-6.8 0.4-3.2 3.3-9.6 5.2-11.9 0.2-0.3 0.5-0.3 0.7-0.2 2.5 1.1 6 3.2 9.6 4.5 2.4 0.9 4.8 1.4 7.3 1.4 3.9 0 6.7-1.2 9.5-3.2 5.6-4.6 9-10.8 12.1-17.5 2-4.3 4.1-11.6 4.4-18.3 0.1-4.9-1.1-8.9-4.5-12.2-1.1-0.7-3-2.1-3-2.8 0-4.2 3.9-13 8.9-22.9 0.2-0.7 0.5-1 1.1-0.7 1.1 0.6 3 1.4 4.6 2.4 2.1 1 5.4 2.4 7.1 3.9 0.9 0.4 1 3 0.9 4.4z'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m104.7 27.8c-1.3-1.5-3.3-1.3-6.2-1.5l-1.9 0.2-7-0.2-31.5 0.2 1.5-9.3c2-1.1 5.1-3.5 5.8-6.3 1-2.8 0.2-5.9-2-7.4-2.3-1.9-5.8-2.4-9.3-0.8-1.6 1-4.7 3.4-5.4 6.9-0.8 4.1 2.4 6.7 4.7 7.9l-1.5 9.1-17.2 0.9c-12.3 1.1-16.3 1.2-20.6 4.3-2 1.3-3 4.5-3.4 9.8-0.6 11.3-0.7 18.7-0.6 28.3 0.4 11.2 0 36.6 3 39.8l-1.2 0.3c-3.8 0.6-4 6.2-0.5 6.6l15.5-1 69.7-7.6c2.5-0.4 4.3-0.9 4.6-4.3l3.7-71.5c0-1.9 0.2-3.6-0.2-4.4zm-49.6-17.3c0.3-2.2 2.4-3 3.3-2.8 0.7 0.4 1 1.8 0 2.8-1.5 2-3.3 1.7-3.3 0zm40 90.2c-4 1-5.5 1.5-11.5 2.4-7.7 1-19.7 2.1-31.2 3.4l-33.8 2.9c-0.7 0.2-1-0.4-1-1-0.6-6.5-1.2-20.5-1.5-39.5l0.3-23.3c0.6-7.5 0.7-8.7 4.6-9.7 5.1-0.9 7.4-1.4 14.9-1.8l19.5-0.5 41.1-0.5c1.4 0 1.9 0.4 1.9 1.5l-3.3 66.1z'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m38.9 52.4c-1.8 0-4 1.1-4.5 3.3-1 3.9 1 7.6 4.5 7.7 3.8 0 5-3.8 4.7-6.3-0.2-2-2-4.7-4.7-4.7z'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m73.5 53.9c-1.8 0-4.3 1.5-4.4 4.5-0.1 3.2 2 5.3 4.3 5.3 2.5 0 4.2-1.7 4.2-4.8 0-3.2-1.7-4.8-4.1-5z'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m72.1 77.1c-2.7 3.4-7.2 7.4-14.7 8.3-7.3 0.3-13.9-2.9-20-8.5-3.5-3.4-8 0-6.2 2.7 1.7 2.5 6.4 6.6 10.4 8.8 3.5 2 7.3 3.3 13.8 3.5 4.7 0 9.2-0.8 12.7-2.4 2.9-1.1 5-2.8 6-3.8 2.3-2.1 3.8-4.1 3.5-7.3-0.9-2.5-3.6-2.8-5.5-1.3z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function CrowdStrikeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 768 500' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
@@ -3632,29 +3602,6 @@ export function OpenRouterIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function MondayIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox='0 -50 256 256'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
preserveAspectRatio='xMidYMid'
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d='M31.8458633,153.488694 C20.3244423,153.513586 9.68073708,147.337265 3.98575204,137.321731 C-1.62714067,127.367831 -1.29055839,115.129325 4.86093879,105.498969 L62.2342919,15.4033556 C68.2125882,5.54538256 79.032489,-0.333585033 90.5563073,0.0146553508 C102.071737,0.290611552 112.546041,6.74705604 117.96667,16.9106216 C123.315033,27.0238906 122.646488,39.1914174 116.240607,48.6847625 L58.9037201,138.780375 C52.9943022,147.988884 42.7873202,153.537154 31.8458633,153.488694 L31.8458633,153.488694 Z'
|
||||
fill='#F62B54'
|
||||
/>
|
||||
<path
|
||||
d='M130.25575,153.488484 C118.683837,153.488484 108.035731,147.301291 102.444261,137.358197 C96.8438154,127.431292 97.1804475,115.223704 103.319447,105.620522 L160.583402,15.7315506 C166.47539,5.73210989 177.327374,-0.284878136 188.929728,0.0146553508 C200.598885,0.269918151 211.174058,6.7973526 216.522421,17.0078646 C221.834319,27.2183766 221.056375,39.4588356 214.456008,48.9278699 L157.204209,138.816842 C151.313487,147.985468 141.153618,153.5168 130.25575,153.488484 Z'
|
||||
fill='#FFCC00'
|
||||
/>
|
||||
<ellipse fill='#00CA72' cx='226.465527' cy='125.324379' rx='29.5375538' ry='28.9176274' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function MongoDBIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'>
|
||||
@@ -4711,24 +4658,6 @@ export function IAMIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function IdentityCenterIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 80 80' xmlns='http://www.w3.org/2000/svg'>
|
||||
<defs>
|
||||
<linearGradient x1='0%' y1='100%' x2='100%' y2='0%' id='identityCenterGradient'>
|
||||
<stop stopColor='#BD0816' offset='0%' />
|
||||
<stop stopColor='#FF5252' offset='100%' />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect fill='url(#identityCenterGradient)' width='80' height='80' />
|
||||
<path
|
||||
d='M46.694,46.8194562 C47.376,46.1374562 47.376,45.0294562 46.694,44.3474562 C46.353,44.0074562 45.906,43.8374562 45.459,43.8374562 C45.01,43.8374562 44.563,44.0074562 44.222,44.3474562 C43.542,45.0284562 43.542,46.1384562 44.222,46.8194562 C44.905,47.5014562 46.013,47.4994562 46.694,46.8194562 M47.718,47.1374562 L51.703,51.1204562 L50.996,51.8274562 L49.868,50.6994562 L48.793,51.7754562 L48.086,51.0684562 L49.161,49.9924562 L47.011,47.8444562 C46.545,48.1654562 46.003,48.3294562 45.458,48.3294562 C44.755,48.3294562 44.051,48.0624562 43.515,47.5264562 C42.445,46.4554562 42.445,44.7124562 43.515,43.6404562 C44.586,42.5714562 46.329,42.5694562 47.401,43.6404562 C48.351,44.5904562 48.455,46.0674562 47.718,47.1374562 M53,44.1014562 C53,46.1684562 51.505,47.0934562 50.023,47.0934562 L50.023,46.0934562 C50.487,46.0934562 52,45.9494562 52,44.1014562 C52,43.0044562 51.353,42.3894562 49.905,42.1084562 C49.68,42.0654562 49.514,41.8754562 49.501,41.6484562 C49.446,40.7444562 48.987,40.1124562 48.384,40.1124562 C48.084,40.1124562 47.854,40.2424562 47.616,40.5464562 C47.506,40.6884562 47.324,40.7594562 47.147,40.7324562 C46.968,40.7054562 46.818,40.5844562 46.755,40.4144562 C46.577,39.9434562 46.211,39.4334562 45.723,38.9774562 C45.231,38.5094562 43.883,37.5074562 41.972,38.2734562 C40.885,38.7054562 40.034,39.9494562 40.034,41.1074562 C40.034,41.2354562 40.043,41.3624562 40.058,41.4884562 C40.061,41.5094562 40.062,41.5304562 40.062,41.5514562 C40.062,41.7994562 39.882,42.0064562 39.645,42.0464562 C38.886,42.2394562 38,42.7454562 38,44.0554562 L38.005,44.2104562 C38.069,45.3254562 39.252,45.9954562 40.358,45.9984562 L41,45.9984562 L41,46.9984562 L40.357,46.9984562 C38.536,46.9944562 37.095,45.8194562 37.006,44.2644562 C37.003,44.1944562 37,44.1244562 37,44.0554562 C37,42.6944562 37.752,41.6484562 39.035,41.1884562 C39.034,41.1614562 39.034,41.1344562 39.034,41.1074562 C39.034,39.5434562 40.138,37.9254562 41.602,37.3434562 C43.298,36.6654562 45.095,37.0034562 46.409,38.2494562 C46.706,38.5274562 47.076,38.9264562 47.372,39.4134562 C47.673,39.2124562 48.008,39.1124562 48.384,39.1124562 C49.257,39.1124562 50.231,39.7714562 50.458,41.2074562 C52.145,41.6324562 53,42.6054562 53,44.1014562 M27,53 L27,27 L53,27 L53,34 L51,34 L51,29 L29,29 L29,51 L51,51 L51,46 L53,46 L53,53 Z'
|
||||
fill='#FFFFFF'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function STSIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 80 80' xmlns='http://www.w3.org/2000/svg'>
|
||||
@@ -4747,24 +4676,6 @@ export function STSIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function SESIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 80 80' xmlns='http://www.w3.org/2000/svg'>
|
||||
<defs>
|
||||
<linearGradient x1='0%' y1='100%' x2='100%' y2='0%' id='sesGradient'>
|
||||
<stop stopColor='#BD0816' offset='0%' />
|
||||
<stop stopColor='#FF5252' offset='100%' />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect fill='url(#sesGradient)' width='80' height='80' />
|
||||
<path
|
||||
d='M57,60.999875 C57,59.373846 55.626,57.9998214 54,57.9998214 C52.374,57.9998214 51,59.373846 51,60.999875 C51,62.625904 52.374,63.9999286 54,63.9999286 C55.626,63.9999286 57,62.625904 57,60.999875 L57,60.999875 Z M40,59.9998571 C38.374,59.9998571 37,61.3738817 37,62.9999107 C37,64.6259397 38.374,65.9999643 40,65.9999643 C41.626,65.9999643 43,64.6259397 43,62.9999107 C43,61.3738817 41.626,59.9998571 40,59.9998571 L40,59.9998571 Z M26,57.9998214 C24.374,57.9998214 23,59.373846 23,60.999875 C23,62.625904 24.374,63.9999286 26,63.9999286 C27.626,63.9999286 29,62.625904 29,60.999875 C29,59.373846 27.626,57.9998214 26,57.9998214 L26,57.9998214 Z M28.605,42.9995536 L51.395,42.9995536 L43.739,36.1104305 L40.649,38.7584778 C40.463,38.9194807 40.23,38.9994821 39.999,38.9994821 C39.768,38.9994821 39.535,38.9194807 39.349,38.7584778 L36.26,36.1104305 L28.605,42.9995536 Z M27,28.1732888 L27,41.7545313 L34.729,34.7984071 L27,28.1732888 Z M51.297,26.9992678 L28.703,26.9992678 L39.999,36.6824408 L51.297,26.9992678 Z M53,41.7545313 L53,28.1732888 L45.271,34.7974071 L53,41.7545313 Z M59,60.999875 C59,63.7099234 56.71,65.9999643 54,65.9999643 C51.29,65.9999643 49,63.7099234 49,60.999875 C49,58.6308327 50.75,56.5837961 53,56.1057876 L53,52.9997321 L41,52.9997321 L41,58.1058233 C43.25,58.5838319 45,60.6308684 45,62.9999107 C45,65.7099591 42.71,68 40,68 C37.29,68 35,65.7099591 35,62.9999107 C35,60.6308684 36.75,58.5838319 39,58.1058233 L39,52.9997321 L27,52.9997321 L27,56.1057876 C29.25,56.5837961 31,58.6308327 31,60.999875 C31,63.7099234 28.71,65.9999643 26,65.9999643 C23.29,65.9999643 21,63.7099234 21,60.999875 C21,58.6308327 22.75,56.5837961 25,56.1057876 L25,51.9997143 C25,51.4477044 25.447,50.9996964 26,50.9996964 L39,50.9996964 L39,44.9995893 L26,44.9995893 C25.447,44.9995893 25,44.5515813 25,43.9995714 L25,25.99925 C25,25.4472401 25.447,24.9992321 26,24.9992321 L54,24.9992321 C54.553,24.9992321 55,25.4472401 55,25.99925 L55,43.9995714 C55,44.5515813 54.553,44.9995893 54,44.9995893 L41,44.9995893 L41,50.9996964 L54,50.9996964 C54.553,50.9996964 55,51.4477044 55,51.9997143 L55,56.1057876 C57.25,56.5837961 59,58.6308327 59,60.999875 L59,60.999875 Z M68,39.9995 C68,45.9066055 66.177,51.5597064 62.727,56.3447919 L61.104,55.174771 C64.307,50.7316916 66,45.4845979 66,39.9995 C66,25.664244 54.337,14.0000357 40.001,14.0000357 C25.664,14.0000357 14,25.664244 14,39.9995 C14,45.4845979 15.693,50.7316916 18.896,55.174771 L17.273,56.3447919 C13.823,51.5597064 12,45.9066055 12,39.9995 C12,24.5612243 24.561,12 39.999,12 C55.438,12 68,24.5612243 68,39.9995 L68,39.9995 Z'
|
||||
fill='#FFFFFF'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SecretsManagerIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 80 80' xmlns='http://www.w3.org/2000/svg'>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { cn, getAssetUrl } from '@/lib/utils'
|
||||
import { Lightbox } from './lightbox'
|
||||
|
||||
@@ -50,14 +50,11 @@ export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProp
|
||||
}
|
||||
|
||||
export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProps) {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const startTimeRef = useRef(0)
|
||||
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
|
||||
const resolvedSrc = getAssetUrl(src)
|
||||
|
||||
const handleClick = () => {
|
||||
if (enableLightbox) {
|
||||
startTimeRef.current = videoRef.current?.currentTime ?? 0
|
||||
setIsLightboxOpen(true)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +62,6 @@ export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProp
|
||||
return (
|
||||
<>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={resolvedSrc}
|
||||
autoPlay
|
||||
loop
|
||||
@@ -84,7 +80,6 @@ export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProp
|
||||
src={src}
|
||||
alt={alt}
|
||||
type='video'
|
||||
startTime={startTimeRef.current}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
195
apps/docs/components/ui/animated-blocks.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
const RX = '2.59574'
|
||||
|
||||
interface BlockRect {
|
||||
opacity: number
|
||||
width: string
|
||||
height: string
|
||||
fill: string
|
||||
x?: string
|
||||
y?: string
|
||||
transform?: string
|
||||
}
|
||||
|
||||
const RECTS = {
|
||||
topRight: [
|
||||
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '33.7252', fill: '#2ABBF8' },
|
||||
{ opacity: 0.6, x: '0', y: '0', width: '85.3433', height: '16.8626', fill: '#2ABBF8' },
|
||||
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
|
||||
{ opacity: 0.6, x: '34.2403', y: '0', width: '34.2403', height: '33.7252', fill: '#2ABBF8' },
|
||||
{ opacity: 1, x: '34.2403', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
|
||||
{
|
||||
opacity: 1,
|
||||
x: '51.6188',
|
||||
y: '16.8626',
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#2ABBF8',
|
||||
},
|
||||
{ opacity: 1, x: '68.4812', y: '0', width: '54.6502', height: '16.8626', fill: '#00F701' },
|
||||
{ opacity: 0.6, x: '106.268', y: '0', width: '34.2403', height: '33.7252', fill: '#00F701' },
|
||||
{ opacity: 0.6, x: '106.268', y: '0', width: '51.103', height: '16.8626', fill: '#00F701' },
|
||||
{
|
||||
opacity: 1,
|
||||
x: '123.6484',
|
||||
y: '16.8626',
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#00F701',
|
||||
},
|
||||
{ opacity: 0.6, x: '157.371', y: '0', width: '34.2403', height: '16.8626', fill: '#FFCC02' },
|
||||
{ opacity: 1, x: '157.371', y: '0', width: '16.8626', height: '16.8626', fill: '#FFCC02' },
|
||||
{ opacity: 0.6, x: '208.993', y: '0', width: '68.4805', height: '16.8626', fill: '#FA4EDF' },
|
||||
{ opacity: 0.6, x: '209.137', y: '0', width: '16.8626', height: '33.7252', fill: '#FA4EDF' },
|
||||
{ opacity: 0.6, x: '243.233', y: '0', width: '34.2403', height: '33.7252', fill: '#FA4EDF' },
|
||||
{ opacity: 1, x: '243.233', y: '0', width: '16.8626', height: '16.8626', fill: '#FA4EDF' },
|
||||
{ opacity: 0.6, x: '260.096', y: '0', width: '34.04', height: '16.8626', fill: '#FA4EDF' },
|
||||
{
|
||||
opacity: 1,
|
||||
x: '260.611',
|
||||
y: '16.8626',
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#FA4EDF',
|
||||
},
|
||||
],
|
||||
bottomLeft: [
|
||||
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '33.7252', fill: '#2ABBF8' },
|
||||
{ opacity: 0.6, x: '0', y: '0', width: '85.3433', height: '16.8626', fill: '#2ABBF8' },
|
||||
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
|
||||
{ opacity: 0.6, x: '34.2403', y: '0', width: '34.2403', height: '33.7252', fill: '#2ABBF8' },
|
||||
{ opacity: 1, x: '34.2403', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
|
||||
{
|
||||
opacity: 1,
|
||||
x: '51.6188',
|
||||
y: '16.8626',
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#2ABBF8',
|
||||
},
|
||||
{ opacity: 1, x: '68.4812', y: '0', width: '54.6502', height: '16.8626', fill: '#00F701' },
|
||||
{ opacity: 0.6, x: '106.268', y: '0', width: '34.2403', height: '33.7252', fill: '#00F701' },
|
||||
{ opacity: 0.6, x: '106.268', y: '0', width: '51.103', height: '16.8626', fill: '#00F701' },
|
||||
{
|
||||
opacity: 1,
|
||||
x: '123.6484',
|
||||
y: '16.8626',
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#00F701',
|
||||
},
|
||||
],
|
||||
bottomRight: [
|
||||
{
|
||||
opacity: 0.6,
|
||||
width: '16.8626',
|
||||
height: '33.726',
|
||||
fill: '#FA4EDF',
|
||||
transform: 'matrix(0 1 1 0 0 0)',
|
||||
},
|
||||
{
|
||||
opacity: 0.6,
|
||||
width: '34.241',
|
||||
height: '16.8626',
|
||||
fill: '#FA4EDF',
|
||||
transform: 'matrix(0 1 1 0 16.891 0)',
|
||||
},
|
||||
{
|
||||
opacity: 0.6,
|
||||
width: '16.8626',
|
||||
height: '68.482',
|
||||
fill: '#FA4EDF',
|
||||
transform: 'matrix(-1 0 0 1 33.739 16.888)',
|
||||
},
|
||||
{
|
||||
opacity: 0.6,
|
||||
width: '16.8626',
|
||||
height: '33.726',
|
||||
fill: '#FA4EDF',
|
||||
transform: 'matrix(0 1 1 0 0 33.776)',
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#FA4EDF',
|
||||
transform: 'matrix(-1 0 0 1 33.739 34.272)',
|
||||
},
|
||||
{
|
||||
opacity: 0.6,
|
||||
width: '16.8626',
|
||||
height: '34.24',
|
||||
fill: '#2ABBF8',
|
||||
transform: 'matrix(-1 0 0 1 33.787 68)',
|
||||
},
|
||||
{
|
||||
opacity: 0.4,
|
||||
width: '16.8626',
|
||||
height: '16.8626',
|
||||
fill: '#1A8FCC',
|
||||
transform: 'matrix(-1 0 0 1 33.787 85)',
|
||||
},
|
||||
],
|
||||
} as const satisfies Record<string, readonly BlockRect[]>
|
||||
|
||||
const GLOBAL_OPACITY = 0.55
|
||||
|
||||
const BlockGroup = memo(function BlockGroup({
|
||||
width,
|
||||
height,
|
||||
viewBox,
|
||||
rects,
|
||||
}: {
|
||||
width: number
|
||||
height: number
|
||||
viewBox: string
|
||||
rects: readonly BlockRect[]
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox={viewBox}
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
className='h-auto w-full'
|
||||
style={{ opacity: GLOBAL_OPACITY }}
|
||||
>
|
||||
{rects.map((r, i) => (
|
||||
<rect
|
||||
key={i}
|
||||
x={r.x}
|
||||
y={r.y}
|
||||
width={r.width}
|
||||
height={r.height}
|
||||
rx={RX}
|
||||
fill={r.fill}
|
||||
transform={r.transform}
|
||||
opacity={r.opacity}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
)
|
||||
})
|
||||
|
||||
export function AnimatedBlocks() {
|
||||
return (
|
||||
<div
|
||||
className='pointer-events-none fixed inset-0 z-0 hidden overflow-hidden lg:block'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<div className='absolute top-[93px] right-0 w-[calc(140px+10.76vw)] max-w-[295px]'>
|
||||
<BlockGroup width={295} height={34} viewBox='0 0 295 34' rects={RECTS.topRight} />
|
||||
</div>
|
||||
|
||||
<div className='-left-24 absolute bottom-0 w-[calc(140px+10.76vw)] max-w-[295px] rotate-180'>
|
||||
<BlockGroup width={295} height={34} viewBox='0 0 295 34' rects={RECTS.bottomLeft} />
|
||||
</div>
|
||||
|
||||
<div className='-bottom-2 absolute right-0 w-[calc(16px+1.25vw)] max-w-[34px]'>
|
||||
<BlockGroup width={34} height={102} viewBox='0 0 34 102' rects={RECTS.bottomRight} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface FAQItem {
|
||||
question: string
|
||||
@@ -32,10 +31,9 @@ function FAQItemRow({
|
||||
className='flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left font-[470] text-[0.875rem] text-[rgba(0,0,0,0.8)] transition-colors hover:bg-[rgba(0,0,0,0.02)] dark:text-[rgba(255,255,255,0.85)] dark:hover:bg-[rgba(255,255,255,0.03)]'
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'h-3.5 w-3.5 shrink-0 text-[rgba(0,0,0,0.3)] transition-transform duration-200 dark:text-[rgba(255,255,255,0.3)]',
|
||||
isOpen && 'rotate-90'
|
||||
)}
|
||||
className={`h-3.5 w-3.5 shrink-0 text-[rgba(0,0,0,0.3)] transition-transform duration-200 dark:text-[rgba(255,255,255,0.3)] ${
|
||||
isOpen ? 'rotate-90' : ''
|
||||
}`}
|
||||
/>
|
||||
{item.question}
|
||||
</button>
|
||||
@@ -83,10 +81,11 @@ export function FAQ({ items, title = 'Common Questions' }: FAQProps) {
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
index !== items.length - 1 &&
|
||||
'border-[rgba(0,0,0,0.08)] border-b dark:border-[rgba(255,255,255,0.08)]'
|
||||
)}
|
||||
className={
|
||||
index !== items.length - 1
|
||||
? 'border-[rgba(0,0,0,0.08)] border-b dark:border-[rgba(255,255,255,0.08)]'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<FAQItemRow
|
||||
item={item}
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { ComponentType, SVGProps } from 'react'
|
||||
import {
|
||||
A2AIcon,
|
||||
AgentMailIcon,
|
||||
AgentPhoneIcon,
|
||||
AgiloftIcon,
|
||||
AhrefsIcon,
|
||||
AirtableIcon,
|
||||
@@ -92,7 +91,6 @@ import {
|
||||
HuggingFaceIcon,
|
||||
HunterIOIcon,
|
||||
IAMIcon,
|
||||
IdentityCenterIcon,
|
||||
ImageIcon,
|
||||
IncidentioIcon,
|
||||
InfisicalIcon,
|
||||
@@ -121,7 +119,6 @@ import {
|
||||
MicrosoftSharepointIcon,
|
||||
MicrosoftTeamsIcon,
|
||||
MistralIcon,
|
||||
MondayIcon,
|
||||
MongoDBIcon,
|
||||
MySQLIcon,
|
||||
Neo4jIcon,
|
||||
@@ -154,7 +151,6 @@ import {
|
||||
RootlyIcon,
|
||||
S3Icon,
|
||||
SalesforceIcon,
|
||||
SESIcon,
|
||||
SearchIcon,
|
||||
SecretsManagerIcon,
|
||||
SendgridIcon,
|
||||
@@ -205,7 +201,6 @@ type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
|
||||
export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
a2a: A2AIcon,
|
||||
agentmail: AgentMailIcon,
|
||||
agentphone: AgentPhoneIcon,
|
||||
agiloft: AgiloftIcon,
|
||||
ahrefs: AhrefsIcon,
|
||||
airtable: AirtableIcon,
|
||||
@@ -298,7 +293,6 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
huggingface: HuggingFaceIcon,
|
||||
hunter: HunterIOIcon,
|
||||
iam: IAMIcon,
|
||||
identity_center: IdentityCenterIcon,
|
||||
image_generator: ImageIcon,
|
||||
imap: MailServerIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
@@ -333,7 +327,6 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
mistral_parse: MistralIcon,
|
||||
mistral_parse_v3: MistralIcon,
|
||||
monday: MondayIcon,
|
||||
mongodb: MongoDBIcon,
|
||||
mysql: MySQLIcon,
|
||||
neo4j: Neo4jIcon,
|
||||
@@ -375,7 +368,6 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
sentry: SentryIcon,
|
||||
serper: SerperIcon,
|
||||
servicenow: ServiceNowIcon,
|
||||
ses: SESIcon,
|
||||
sftp: SftpIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
shopify: ShopifyIcon,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Check } from 'lucide-react'
|
||||
import { useParams, usePathname, useRouter } from 'next/navigation'
|
||||
import {
|
||||
@@ -24,9 +25,24 @@ export function LanguageDropdown() {
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
const langFromParams = params?.lang as string
|
||||
const currentLang =
|
||||
langFromParams && Object.keys(languages).includes(langFromParams) ? langFromParams : 'en'
|
||||
const [currentLang, setCurrentLang] = useState(() => {
|
||||
const langFromParams = params?.lang as string
|
||||
return langFromParams && Object.keys(languages).includes(langFromParams) ? langFromParams : 'en'
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const langFromParams = params?.lang as string
|
||||
|
||||
if (langFromParams && Object.keys(languages).includes(langFromParams)) {
|
||||
if (langFromParams !== currentLang) {
|
||||
setCurrentLang(langFromParams)
|
||||
}
|
||||
} else {
|
||||
if (currentLang !== 'en') {
|
||||
setCurrentLang('en')
|
||||
}
|
||||
}
|
||||
}, [params])
|
||||
|
||||
const handleLanguageChange = (locale: string) => {
|
||||
if (locale === currentLang) return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useLayoutEffect, useRef } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { getAssetUrl } from '@/lib/utils'
|
||||
|
||||
interface LightboxProps {
|
||||
@@ -9,12 +9,10 @@ interface LightboxProps {
|
||||
src: string
|
||||
alt: string
|
||||
type: 'image' | 'video'
|
||||
startTime?: number
|
||||
}
|
||||
|
||||
export function Lightbox({ isOpen, onClose, src, alt, type, startTime }: LightboxProps) {
|
||||
export function Lightbox({ isOpen, onClose, src, alt, type }: LightboxProps) {
|
||||
const overlayRef = useRef<HTMLDivElement>(null)
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
@@ -42,12 +40,6 @@ export function Lightbox({ isOpen, onClose, src, alt, type, startTime }: Lightbo
|
||||
}
|
||||
}, [isOpen, onClose])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isOpen && type === 'video' && videoRef.current && startTime != null && startTime > 0) {
|
||||
videoRef.current.currentTime = startTime
|
||||
}
|
||||
}, [isOpen, startTime, type])
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
@@ -69,7 +61,6 @@ export function Lightbox({ isOpen, onClose, src, alt, type, startTime }: Lightbo
|
||||
/>
|
||||
) : (
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={getAssetUrl(src)}
|
||||
autoPlay
|
||||
loop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useState } from 'react'
|
||||
import { cn, getAssetUrl } from '@/lib/utils'
|
||||
import { useState } from 'react'
|
||||
import { getAssetUrl } from '@/lib/utils'
|
||||
import { Lightbox } from './lightbox'
|
||||
|
||||
interface VideoProps {
|
||||
@@ -12,8 +12,6 @@ interface VideoProps {
|
||||
muted?: boolean
|
||||
playsInline?: boolean
|
||||
enableLightbox?: boolean
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
export function Video({
|
||||
@@ -24,16 +22,11 @@ export function Video({
|
||||
muted = true,
|
||||
playsInline = true,
|
||||
enableLightbox = true,
|
||||
width,
|
||||
height,
|
||||
}: VideoProps) {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const startTimeRef = useRef(0)
|
||||
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
|
||||
|
||||
const handleVideoClick = () => {
|
||||
if (enableLightbox) {
|
||||
startTimeRef.current = videoRef.current?.currentTime ?? 0
|
||||
setIsLightboxOpen(true)
|
||||
}
|
||||
}
|
||||
@@ -41,17 +34,11 @@ export function Video({
|
||||
return (
|
||||
<>
|
||||
<video
|
||||
ref={videoRef}
|
||||
autoPlay={autoPlay}
|
||||
loop={loop}
|
||||
muted={muted}
|
||||
playsInline={playsInline}
|
||||
width={width}
|
||||
height={height}
|
||||
className={cn(
|
||||
className,
|
||||
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-95'
|
||||
)}
|
||||
className={`${className} ${enableLightbox ? 'cursor-pointer transition-opacity hover:opacity-95' : ''}`}
|
||||
src={getAssetUrl(src)}
|
||||
onClick={handleVideoClick}
|
||||
/>
|
||||
@@ -63,7 +50,6 @@ export function Video({
|
||||
src={src}
|
||||
alt={`Video: ${src}`}
|
||||
type='video'
|
||||
startTime={startTimeRef.current}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -5,7 +5,6 @@ description: Your per-workflow AI assistant for building and editing workflows.
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
Copilot is the AI assistant built into every workflow editor. It is scoped to the workflow you have open — it reads the current structure, makes changes directly, and saves checkpoints so you can revert if needed.
|
||||
@@ -16,7 +15,7 @@ For workspace-wide tasks (managing multiple workflows, running research, working
|
||||
Copilot is a Sim-managed service. For self-hosted deployments, go to [sim.ai](https://sim.ai) → Settings → Copilot, generate a Copilot API key, then set `COPILOT_API_KEY` in your self-hosted environment.
|
||||
</Callout>
|
||||
|
||||
<Video src="copilot/copilot.mp4" width={700} height={450} />
|
||||
{/* TODO: Screenshot of the workflow editor with the Copilot panel open on the right side — showing a conversation with a workflow change applied. Ideally shows a message from the user, a response from Copilot, and the checkpoint icon visible on the message. */}
|
||||
|
||||
## What Copilot Can Do
|
||||
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
---
|
||||
title: Access Control
|
||||
description: Restrict which models, blocks, and platform features each group of users can access
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
import { Image } from '@/components/ui/image'
|
||||
|
||||
Access Control lets workspace admins define permission groups that restrict what each set of workspace members can do — which AI model providers they can use, which workflow blocks they can place, and which platform features are visible to them. Permission groups are scoped to a single workspace: a user can be in different groups (or no group) in different workspaces. Restrictions are enforced both in the workflow executor and in Mothership, based on the workflow's workspace.
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
Access control is built around **permission groups**. Each group belongs to a specific workspace and has a name, an optional description, and a configuration that defines what its members can and cannot do. A user can belong to at most one permission group **per workspace**, but can belong to different groups in different workspaces.
|
||||
|
||||
When a user runs a workflow or uses Mothership, Sim reads their group's configuration and applies it:
|
||||
|
||||
- **In the executor:** If a workflow uses a disallowed block type or model provider, execution halts immediately with an error. This applies to both manual runs and scheduled or API-triggered deployments.
|
||||
- **In Mothership:** Disallowed blocks are filtered out of the block list so they cannot be added to a workflow. Disallowed tool types (MCP, custom tools, skills) are skipped if Mothership attempts to use them.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Open Access Control settings
|
||||
|
||||
Go to **Settings → Enterprise → Access Control** in the workspace you want to manage. Each workspace has its own set of permission groups.
|
||||
|
||||
<Image src="/static/enterprise/access-control-groups.png" alt="Access Control settings showing a list of permission groups: Contractors, Sales, Engineering, and Marketing, each with Details and Delete actions" width={900} height={500} />
|
||||
|
||||
### 2. Create a permission group
|
||||
|
||||
Click **+ Create** and enter a name (required) and optional description. You can also enable **Auto-add new members** — when active, any new member who joins this workspace is automatically added to this group. Only one group per workspace can have this setting enabled at a time.
|
||||
|
||||
### 3. Configure permissions
|
||||
|
||||
Click **Details** on a group, then open **Configure Permissions**. There are three tabs.
|
||||
|
||||
#### Model Providers
|
||||
|
||||
Controls which AI model providers members of this group can use.
|
||||
|
||||
<Image src="/static/enterprise/access-control-model-providers.png" alt="Model Providers tab showing a grid of AI providers including Ollama, vLLM, OpenAI, Anthropic, Google, Azure OpenAI, and others with checkboxes to allow or restrict access" width={900} height={500} /> The list shows all providers available in Sim.
|
||||
|
||||
- **All checked (default):** All providers are allowed.
|
||||
- **Subset checked:** Only the selected providers are allowed. Any workflow block or agent using a provider not on the list will fail at execution time.
|
||||
|
||||
#### Blocks
|
||||
|
||||
Controls which workflow blocks members can place and execute.
|
||||
|
||||
<Image src="/static/enterprise/access-control-blocks.png" alt="Blocks tab showing Core Blocks (Agent, API, Condition, Function, Knowledge, etc.) and Tools (integrations like 1Password, A2A, Ahrefs, Airtable, and more) with checkboxes to allow or restrict each" width={900} height={500} /> Blocks are split into two sections: **Core Blocks** (Agent, API, Condition, Function, etc.) and **Tools** (all integration blocks).
|
||||
|
||||
- **All checked (default):** All blocks are allowed.
|
||||
- **Subset checked:** Only the selected blocks are allowed. Workflows that already contain a disallowed block will fail when run — they are not automatically modified.
|
||||
|
||||
<Callout type="info">
|
||||
The `start_trigger` block (the entry point of every workflow) is always allowed and cannot be restricted.
|
||||
</Callout>
|
||||
|
||||
#### Platform
|
||||
|
||||
Controls visibility of platform features and modules.
|
||||
|
||||
<Image src="/static/enterprise/access-control-platform.png" alt="Platform tab showing feature toggles grouped by category: Sidebar (Knowledge Base, Tables, Templates), Workflow Panel (Copilot), Settings Tabs, Tools, Deploy Tabs, Features, Logs, and Collaboration" width={900} height={500} /> Each checkbox maps to a specific feature; checking it hides or disables that feature for group members.
|
||||
|
||||
**Sidebar**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Knowledge Base | Hides the Knowledge Base section from the sidebar |
|
||||
| Tables | Hides the Tables section from the sidebar |
|
||||
| Templates | Hides the Templates section from the sidebar |
|
||||
|
||||
**Workflow Panel**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Copilot | Hides the Copilot panel inside the workflow editor |
|
||||
|
||||
**Settings Tabs**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Integrations | Hides the Integrations tab in Settings |
|
||||
| Secrets | Hides the Secrets tab in Settings |
|
||||
| API Keys | Hides the Sim Keys tab in Settings |
|
||||
| Files | Hides the Files tab in Settings |
|
||||
|
||||
**Tools**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| MCP Tools | Disables the use of MCP tools in workflows and agents |
|
||||
| Custom Tools | Disables the use of custom tools in workflows and agents |
|
||||
| Skills | Disables the use of Sim Skills in workflows and agents |
|
||||
|
||||
**Deploy Tabs**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| API | Hides the API deployment tab |
|
||||
| MCP | Hides the MCP deployment tab |
|
||||
| A2A | Hides the A2A deployment tab |
|
||||
| Chat | Hides the Chat deployment tab |
|
||||
| Template | Hides the Template deployment tab |
|
||||
|
||||
**Features**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Sim Mailer | Hides the Sim Mailer (Inbox) feature |
|
||||
| Public API | Disables public API access for deployed workflows |
|
||||
|
||||
**Logs**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Trace Spans | Hides trace span details in execution logs |
|
||||
|
||||
**Collaboration**
|
||||
|
||||
| Feature | Effect when checked |
|
||||
|---------|-------------------|
|
||||
| Invitations | Disables the ability to invite new members to the workspace |
|
||||
|
||||
### 4. Add members
|
||||
|
||||
Open the group's **Details** view and add members by searching for users by name or email. Only users who already have workspace-level access can be added. A user can only belong to one group per workspace — adding a user to a new group within the same workspace removes them from their current group for that workspace.
|
||||
|
||||
---
|
||||
|
||||
## Enforcement
|
||||
|
||||
### Workflow execution
|
||||
|
||||
Restrictions are enforced at the point of execution, not at save time. If a group's configuration changes after a workflow is built:
|
||||
|
||||
- **Block restrictions:** Any workflow run that reaches a disallowed block halts immediately with an error. The workflow is not modified — only execution is blocked.
|
||||
- **Model provider restrictions:** Any block or agent that uses a disallowed provider halts immediately with an error.
|
||||
- **Tool restrictions (MCP, custom tools, skills):** Agents that use a disallowed tool type halt immediately with an error.
|
||||
|
||||
This applies regardless of how the workflow is triggered — manually, via API, via schedule, or via webhook.
|
||||
|
||||
### Mothership
|
||||
|
||||
When a user opens Mothership, their permission group is read before any block or tool suggestions are made:
|
||||
|
||||
- Blocks not in the allowed list are filtered out of the block picker entirely — they do not appear as options.
|
||||
- If Mothership generates a workflow step that would use a disallowed tool (MCP, custom, or skills), that step is skipped and the reason is noted.
|
||||
|
||||
---
|
||||
|
||||
## User membership rules
|
||||
|
||||
- A user can belong to **at most one** permission group **per workspace**, but may be in different groups across different workspaces.
|
||||
- Moving a user to a new group within a workspace automatically removes them from their previous group in that workspace.
|
||||
- Users not assigned to any group in a workspace have no restrictions applied in that workspace (all blocks, providers, and features are available to them there).
|
||||
- If **Auto-add new members** is enabled on a group, new members of that workspace are automatically placed in the group. Only one group per workspace can have this setting active.
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{
|
||||
question: "Who can create and manage permission groups?",
|
||||
answer: "Any workspace admin on an Enterprise-entitled workspace can create, edit, and delete permission groups for that workspace. The workspace's billed account must be on the Enterprise plan."
|
||||
},
|
||||
{
|
||||
question: "What happens to a workflow that was built before a block was restricted?",
|
||||
answer: "The workflow is not modified — it still exists and can be edited. However, any run that reaches a disallowed block will halt immediately with an error. The block must be removed or the user's group configuration must be updated before the workflow can run successfully."
|
||||
},
|
||||
{
|
||||
question: "Can a user be in multiple permission groups?",
|
||||
answer: "A user can belong to at most one permission group per workspace, but can belong to different groups in different workspaces. Adding a user to a new group within the same workspace automatically removes them from their previous group in that workspace."
|
||||
},
|
||||
{
|
||||
question: "What does a user see if they have no permission group assigned in a workspace?",
|
||||
answer: "Users with no group in a given workspace have no restrictions in that workspace. All blocks, model providers, and platform features are fully available to them there. Restrictions only apply in the specific workspaces where they are assigned to a group."
|
||||
},
|
||||
{
|
||||
question: "Does Mothership respect the same restrictions as the executor?",
|
||||
answer: "Yes. Mothership reads the user's permission group for the active workspace before suggesting blocks or tools. Disallowed blocks are filtered out of the block picker, and disallowed tool types are skipped during workflow generation."
|
||||
},
|
||||
{
|
||||
question: "Can I restrict access to specific workflows or workspaces?",
|
||||
answer: "Access Control operates at the feature and block level within a workspace. To restrict who can access the workspace itself, use workspace invitations and permissions. To apply different restrictions to different workflows, put them in different workspaces with distinct permission groups."
|
||||
},
|
||||
{
|
||||
question: "What is Auto-add new members?",
|
||||
answer: "When a group has Auto-add new members enabled, any new member who joins the workspace is automatically added to that group. Only one group per workspace can have this setting enabled at a time."
|
||||
}
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
Self-hosted deployments use environment variables instead of the billing/plan check.
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
ACCESS_CONTROL_ENABLED=true
|
||||
NEXT_PUBLIC_ACCESS_CONTROL_ENABLED=true
|
||||
```
|
||||
|
||||
You can also set a server-level block allowlist using the `ALLOWED_INTEGRATIONS` environment variable. This is applied as an additional constraint on top of any permission group configuration — a block must be allowed by both the environment allowlist and the user's group to be usable.
|
||||
|
||||
```bash
|
||||
# Only these block types are available across the entire instance
|
||||
ALLOWED_INTEGRATIONS=slack,gmail,agent,function,condition
|
||||
```
|
||||
|
||||
Once enabled, permission groups are managed through **Settings → Enterprise → Access Control** the same way as Sim Cloud.
|
||||
@@ -1,143 +0,0 @@
|
||||
---
|
||||
title: Audit Logs
|
||||
description: Track every action taken across your organization's workspaces
|
||||
---
|
||||
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
import { Image } from '@/components/ui/image'
|
||||
|
||||
Audit logs give your organization a tamper-evident record of every significant action taken across workspaces — who did what, when, and on which resource. Use them for security reviews, compliance investigations, and incident response.
|
||||
|
||||
---
|
||||
|
||||
## Viewing audit logs
|
||||
|
||||
### In the UI
|
||||
|
||||
Go to **Settings → Enterprise → Audit Logs** in your workspace. Logs are displayed in a table with the following columns:
|
||||
|
||||
<Image src="/static/enterprise/audit-logs.png" alt="Audit Logs settings showing a table of events with columns for Timestamp, Event, Description, and Actor, along with search and filter controls" width={900} height={500} />
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| **Timestamp** | When the action occurred. |
|
||||
| **Event** | The action taken, e.g. `workflow.created`. |
|
||||
| **Description** | A human-readable summary of the action. |
|
||||
| **Actor** | The email address of the user who performed the action. |
|
||||
|
||||
Use the search bar, event type filter, and date range selector to narrow results.
|
||||
|
||||
### Via API
|
||||
|
||||
Audit logs are also accessible through the Sim API for integration with external SIEM or log management tools.
|
||||
|
||||
```http
|
||||
GET /api/v1/audit-logs
|
||||
Authorization: Bearer <api-key>
|
||||
```
|
||||
|
||||
**Query parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `action` | string | Filter by event type (e.g. `workflow.created`) |
|
||||
| `resourceType` | string | Filter by resource type (e.g. `workflow`) |
|
||||
| `resourceId` | string | Filter by a specific resource ID |
|
||||
| `workspaceId` | string | Filter by workspace |
|
||||
| `actorId` | string | Filter by user ID (must be an org member) |
|
||||
| `startDate` | string | ISO 8601 date — return logs on or after this date |
|
||||
| `endDate` | string | ISO 8601 date — return logs on or before this date |
|
||||
| `includeDeparted` | boolean | Include logs from members who have since left the organization (default `false`) |
|
||||
| `limit` | number | Results per page (1–100, default 50) |
|
||||
| `cursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
**Example response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "abc123",
|
||||
"action": "workflow.created",
|
||||
"resourceType": "workflow",
|
||||
"resourceId": "wf_xyz",
|
||||
"resourceName": "Customer Onboarding",
|
||||
"description": "Created workflow \"Customer Onboarding\"",
|
||||
"actorId": "usr_abc",
|
||||
"actorName": "Alice Smith",
|
||||
"actorEmail": "alice@company.com",
|
||||
"workspaceId": "ws_def",
|
||||
"metadata": {},
|
||||
"createdAt": "2026-04-20T21:16:00.000Z"
|
||||
}
|
||||
],
|
||||
"nextCursor": "eyJpZCI6ImFiYzEyMyJ9"
|
||||
}
|
||||
```
|
||||
|
||||
Paginate by passing the `nextCursor` value as the `cursor` parameter in the next request. When `nextCursor` is absent, you have reached the last page.
|
||||
|
||||
The API accepts both personal and workspace-scoped API keys. Rate limits apply — the response includes `X-RateLimit-*` headers with your current limit and remaining quota.
|
||||
|
||||
---
|
||||
|
||||
## Event types
|
||||
|
||||
Audit log events follow a `resource.action` naming pattern. The table below lists the main categories.
|
||||
|
||||
| Category | Example events |
|
||||
|----------|---------------|
|
||||
| **Workflows** | `workflow.created`, `workflow.deleted`, `workflow.deployed`, `workflow.locked` |
|
||||
| **Workspaces** | `workspace.created`, `workspace.updated`, `workspace.deleted` |
|
||||
| **Members** | `member.invited`, `member.removed`, `member.role_changed` |
|
||||
| **Permission groups** | `permission_group.created`, `permission_group.updated`, `permission_group.deleted` |
|
||||
| **Environments** | `environment.updated`, `environment.deleted` |
|
||||
| **Knowledge bases** | `knowledge_base.created`, `knowledge_base.deleted`, `connector.synced` |
|
||||
| **Tables** | `table.created`, `table.updated`, `table.deleted` |
|
||||
| **API keys** | `api_key.created`, `api_key.revoked` |
|
||||
| **Credentials** | `credential.created`, `credential.deleted`, `oauth.disconnected` |
|
||||
| **Organization** | `organization.updated`, `org_member.added`, `org_member.role_changed` |
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{
|
||||
question: "Who can view audit logs?",
|
||||
answer: "Organization owners and admins can view audit logs. On Sim Cloud, you must be on the Enterprise plan."
|
||||
},
|
||||
{
|
||||
question: "Are audit logs tamper-proof?",
|
||||
answer: "Audit log entries are append-only and cannot be modified or deleted through the Sim interface or API. They represent a reliable record of actions taken in your organization."
|
||||
},
|
||||
{
|
||||
question: "Can I export audit logs?",
|
||||
answer: "Yes. Use the API to export logs programmatically. Paginate through all records using the cursor parameter and store them in your own data warehouse or SIEM."
|
||||
},
|
||||
{
|
||||
question: "Are logs scoped to a single workspace or the whole organization?",
|
||||
answer: "Audit logs are scoped to your organization and include activity across all workspaces within it. You can filter by workspaceId to narrow results to a specific workspace."
|
||||
},
|
||||
{
|
||||
question: "What information is included in each log entry?",
|
||||
answer: "Each entry includes the event type, a description, the actor's name and email, the affected resource, the workspace, and a timestamp. IP addresses and user agents are not exposed through the API."
|
||||
},
|
||||
{
|
||||
question: "Can I filter logs by a specific user?",
|
||||
answer: "Yes. Pass the actorId query parameter to filter logs by a specific user. The actor must be a current or former member of your organization."
|
||||
}
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
Self-hosted deployments use environment variables instead of the billing/plan check.
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
AUDIT_LOGS_ENABLED=true
|
||||
NEXT_PUBLIC_AUDIT_LOGS_ENABLED=true
|
||||
```
|
||||
|
||||
Once enabled, audit logs are viewable in **Settings → Enterprise → Audit Logs** and accessible via the API.
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
title: Data Retention
|
||||
description: Control how long execution logs, deleted resources, and copilot data are kept before permanent deletion
|
||||
---
|
||||
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
import { Image } from '@/components/ui/image'
|
||||
|
||||
Data Retention lets organization owners and admins on Enterprise plans configure how long three categories of data are kept before they are permanently deleted. The configuration applies to every workspace in the organization.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
Go to **Settings → Enterprise → Data Retention** in your workspace.
|
||||
|
||||
<Image src="/static/enterprise/data-retention.png" alt="Data Retention settings showing three dropdowns — Log retention, Soft deletion cleanup, and Task cleanup — each set to Forever" width={900} height={500} />
|
||||
|
||||
You will see three independent settings, each with the same set of options: **1 day, 3 days, 7 days, 14 days, 30 days, 60 days, 90 days, 180 days, 1 year, 5 years,** or **Forever**.
|
||||
|
||||
Setting a period to **Forever** means that category of data is never automatically deleted.
|
||||
|
||||
---
|
||||
|
||||
## Settings
|
||||
|
||||
### Log retention
|
||||
|
||||
Controls how long **workflow execution logs** are kept.
|
||||
|
||||
When the retention period expires, execution log records are permanently deleted, along with any files associated with those executions stored in cloud storage.
|
||||
|
||||
### Soft deletion cleanup
|
||||
|
||||
Controls how long **soft-deleted resources** remain recoverable before permanent removal.
|
||||
|
||||
When you delete a workflow, folder, knowledge base, table, or file, it is initially soft-deleted and can be recovered from Recently Deleted. Once the soft deletion cleanup period expires, those resources are permanently removed and cannot be recovered.
|
||||
|
||||
Resources covered:
|
||||
|
||||
- Workflows
|
||||
- Workflow folders
|
||||
- Knowledge bases
|
||||
- Tables
|
||||
- Files
|
||||
- MCP server configurations
|
||||
- Agent memory
|
||||
|
||||
### Task cleanup
|
||||
|
||||
Controls how long **Mothership data** is kept, including:
|
||||
|
||||
- Copilot chats and run history
|
||||
- Run checkpoints and async tool calls
|
||||
- Inbox tasks (Sim Mailer)
|
||||
|
||||
Each setting is independent. You can configure a short log retention period alongside a long soft deletion cleanup period, or any combination that fits your compliance requirements.
|
||||
|
||||
---
|
||||
|
||||
## Organization-wide configuration
|
||||
|
||||
Retention is configured at the **organization level**. A single configuration applies to every workspace in the organization — there are no per-workspace overrides.
|
||||
|
||||
---
|
||||
|
||||
## Defaults
|
||||
|
||||
By default, all three settings are unconfigured — no data is automatically deleted in any category until you configure it. Setting a period to **Forever** has the same effect as leaving it unconfigured, but makes the intent explicit and allows you to change it later without saving from scratch.
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{
|
||||
question: "Who can configure data retention settings?",
|
||||
answer: "Only organization owners and admins can configure data retention settings. On Sim Cloud, the organization must be on an Enterprise plan."
|
||||
},
|
||||
{
|
||||
question: "Is deletion immediate once the retention period expires?",
|
||||
answer: "No. Deletion runs on a scheduled cleanup job. Data is deleted when the job next runs after the retention period has elapsed — not at the exact moment it expires."
|
||||
},
|
||||
{
|
||||
question: "Can deleted data be recovered after the soft deletion cleanup period?",
|
||||
answer: "No. Once the soft deletion cleanup period expires and the cleanup job runs, resources are permanently deleted and cannot be recovered."
|
||||
},
|
||||
{
|
||||
question: "Does the retention period apply to all workspaces in my organization?",
|
||||
answer: "Yes. Retention is configured once per organization and applies to every workspace in the organization."
|
||||
},
|
||||
{
|
||||
question: "What happens if I shorten the retention period?",
|
||||
answer: "The next cleanup job will delete any data that is older than the new, shorter period — including data that would have been kept under the previous setting. Shortening the period is irreversible for data that falls outside the new window."
|
||||
},
|
||||
{
|
||||
question: "What is the minimum retention period?",
|
||||
answer: "1 day (24 hours)."
|
||||
},
|
||||
{
|
||||
question: "What is the maximum retention period?",
|
||||
answer: "5 years."
|
||||
}
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_DATA_RETENTION_ENABLED=true
|
||||
```
|
||||
|
||||
Once enabled, data retention settings are configurable through **Settings → Enterprise → Data Retention** the same way as Sim Cloud.
|
||||
@@ -3,6 +3,7 @@ title: Enterprise
|
||||
description: Enterprise features for business organizations
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
Sim Enterprise provides advanced features for organizations with enhanced security, compliance, and management requirements.
|
||||
@@ -11,7 +12,7 @@ Sim Enterprise provides advanced features for organizations with enhanced securi
|
||||
|
||||
## Access Control
|
||||
|
||||
Define permission groups on a workspace to control what features and integrations its members can use. Permission groups are scoped to a single workspace — a user can belong to different groups (or no group) in different workspaces.
|
||||
Define permission groups to control what features and integrations team members can use.
|
||||
|
||||
### Features
|
||||
|
||||
@@ -21,64 +22,104 @@ Define permission groups on a workspace to control what features and integration
|
||||
|
||||
### Setup
|
||||
|
||||
1. Navigate to **Settings** → **Access Control** in the workspace you want to manage
|
||||
1. Navigate to **Settings** → **Access Control** in your workspace
|
||||
2. Create a permission group with your desired restrictions
|
||||
3. Add workspace members to the permission group
|
||||
3. Add team members to the permission group
|
||||
|
||||
Any workspace admin on an Enterprise-entitled workspace can manage permission groups. Users not assigned to any group have full access. Restrictions are enforced at both UI and execution time, based on the workflow's workspace.
|
||||
|
||||
See the [Access Control guide](/docs/enterprise/access-control) for full details.
|
||||
<Callout type="info">
|
||||
Users not assigned to any permission group have full access. Permission restrictions are enforced at both UI and execution time.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Single Sign-On (SSO)
|
||||
|
||||
Enterprise authentication with SAML 2.0 and OIDC support. Works with Okta, Azure AD (Entra ID), Google Workspace, ADFS, and any standard OIDC or SAML 2.0 provider.
|
||||
Enterprise authentication with SAML 2.0 and OIDC support for centralized identity management.
|
||||
|
||||
See the [SSO setup guide](/docs/enterprise/sso) for step-by-step instructions and provider-specific configuration.
|
||||
### Supported Providers
|
||||
|
||||
- Okta
|
||||
- Azure AD / Entra ID
|
||||
- Google Workspace
|
||||
- OneLogin
|
||||
- Any SAML 2.0 or OIDC provider
|
||||
|
||||
### Setup
|
||||
|
||||
1. Navigate to **Settings** → **SSO** in your workspace
|
||||
2. Choose your identity provider
|
||||
3. Configure the connection using your IdP's metadata
|
||||
4. Enable SSO for your organization
|
||||
|
||||
<Callout type="info">
|
||||
Once SSO is enabled, team members authenticate through your identity provider instead of email/password.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Whitelabeling
|
||||
## Self-Hosted Configuration
|
||||
|
||||
Replace Sim's default branding — logos, product name, and favicons — with your own. See the [whitelabeling guide](/docs/enterprise/whitelabeling).
|
||||
For self-hosted deployments, enterprise features can be enabled via environment variables without requiring billing.
|
||||
|
||||
---
|
||||
|
||||
## Audit Logs
|
||||
|
||||
Track configuration and security-relevant actions across your organization for compliance and monitoring. See the [audit logs guide](/docs/enterprise/audit-logs).
|
||||
|
||||
---
|
||||
|
||||
## Data Retention
|
||||
|
||||
Configure how long execution logs, soft-deleted resources, and Mothership data are kept before permanent deletion. See the [data retention guide](/docs/enterprise/data-retention).
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "Who can manage Enterprise features?", answer: "Workspace admins on an Enterprise-entitled workspace. Access Control, SSO, whitelabeling, audit logs, and data retention are all configured per workspace under Settings → Enterprise." },
|
||||
{ question: "Which SSO providers are supported?", answer: "Sim supports SAML 2.0 and OIDC, which works with virtually any enterprise identity provider including Okta, Azure AD (Entra ID), Google Workspace, ADFS, and OneLogin." },
|
||||
{ question: "How do access control permission groups work?", answer: "Permission groups are created per workspace and let you restrict which AI providers, workflow blocks, and platform features are available to specific members of that workspace. Each user can belong to at most one group per workspace. Users not assigned to any group have full access. Restrictions are enforced at both the UI level and at execution time based on the workflow's workspace." },
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
Self-hosted deployments enable enterprise features via environment variables instead of billing.
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `ORGANIZATIONS_ENABLED`, `NEXT_PUBLIC_ORGANIZATIONS_ENABLED` | Team and organization management |
|
||||
| `ACCESS_CONTROL_ENABLED`, `NEXT_PUBLIC_ACCESS_CONTROL_ENABLED` | Permission groups |
|
||||
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | SAML and OIDC sign-in |
|
||||
| `WHITELABELING_ENABLED`, `NEXT_PUBLIC_WHITELABELING_ENABLED` | Custom branding |
|
||||
| `AUDIT_LOGS_ENABLED`, `NEXT_PUBLIC_AUDIT_LOGS_ENABLED` | Audit logging |
|
||||
| `NEXT_PUBLIC_DATA_RETENTION_ENABLED` | Data retention configuration |
|
||||
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling groups for email triggers |
|
||||
| `INBOX_ENABLED`, `NEXT_PUBLIC_INBOX_ENABLED` | Sim Mailer inbox |
|
||||
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Disable invitations; manage membership via Admin API |
|
||||
| `ORGANIZATIONS_ENABLED`, `NEXT_PUBLIC_ORGANIZATIONS_ENABLED` | Enable team/organization management |
|
||||
| `ACCESS_CONTROL_ENABLED`, `NEXT_PUBLIC_ACCESS_CONTROL_ENABLED` | Permission groups for access restrictions |
|
||||
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Single Sign-On with SAML/OIDC |
|
||||
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling Groups for email triggers |
|
||||
| `INBOX_ENABLED`, `NEXT_PUBLIC_INBOX_ENABLED` | Sim Mailer inbox for outbound email |
|
||||
| `WHITELABELING_ENABLED`, `NEXT_PUBLIC_WHITELABELING_ENABLED` | Custom branding and white-labeling |
|
||||
| `AUDIT_LOGS_ENABLED`, `NEXT_PUBLIC_AUDIT_LOGS_ENABLED` | Audit logging for compliance and monitoring |
|
||||
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Globally disable workspace/organization invitations |
|
||||
|
||||
Once enabled, each feature is configured through the same Settings UI as Sim Cloud. When invitations are disabled, use the Admin API (`x-admin-key` header) to manage organization and workspace membership.
|
||||
### Organization Management
|
||||
|
||||
When billing is disabled, use the Admin API to manage organizations:
|
||||
|
||||
```bash
|
||||
# Create an organization
|
||||
curl -X POST https://your-instance/api/v1/admin/organizations \
|
||||
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "My Organization", "ownerId": "user-id-here"}'
|
||||
|
||||
# Add a member
|
||||
curl -X POST https://your-instance/api/v1/admin/organizations/{orgId}/members \
|
||||
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"userId": "user-id-here", "role": "admin"}'
|
||||
```
|
||||
|
||||
### Workspace Members
|
||||
|
||||
When invitations are disabled, use the Admin API to manage workspace memberships directly:
|
||||
|
||||
```bash
|
||||
# Add a user to a workspace
|
||||
curl -X POST https://your-instance/api/v1/admin/workspaces/{workspaceId}/members \
|
||||
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"userId": "user-id-here", "permissions": "write"}'
|
||||
|
||||
# Remove a user from a workspace
|
||||
curl -X DELETE "https://your-instance/api/v1/admin/workspaces/{workspaceId}/members?userId=user-id-here" \
|
||||
-H "x-admin-key: YOUR_ADMIN_API_KEY"
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- Enabling `ACCESS_CONTROL_ENABLED` automatically enables organizations, as access control requires organization membership.
|
||||
- When `DISABLE_INVITATIONS` is set, users cannot send invitations. Use the Admin API to manage workspace and organization memberships instead.
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "What are the minimum requirements to self-host Sim?", answer: "The Docker Compose production setup includes the Sim application (8 GB memory limit), a realtime collaboration server (1 GB memory limit), and a PostgreSQL database with pgvector. A machine with at least 16 GB of RAM and 4 CPU cores is recommended. You will also need Docker and Docker Compose installed." },
|
||||
{ question: "Can I run Sim completely offline with local AI models?", answer: "Yes. Sim supports Ollama and VLLM for running local AI models. A separate Docker Compose configuration (docker-compose.ollama.yml) is available for deploying with Ollama. This lets you run workflows without any external API calls, keeping all data on your infrastructure." },
|
||||
{ question: "How does data privacy work with self-hosted deployments?", answer: "When self-hosted, all data stays on your infrastructure. Workflow definitions, execution logs, credentials, and user data are stored in your PostgreSQL database. If you use local AI models through Ollama or VLLM, no data leaves your network. When using external AI providers, only the data sent in prompts goes to those providers." },
|
||||
{ question: "Do I need a paid license to self-host Sim?", answer: "The core Sim platform is open source under Apache 2.0 and can be self-hosted for free. Enterprise features like SSO (SAML/OIDC), access control with permission groups, and organization management require an Enterprise subscription for production use. These features can be enabled via environment variables for development and evaluation without a license." },
|
||||
{ question: "Which SSO providers are supported?", answer: "Sim supports SAML 2.0 and OIDC protocols, which means it works with virtually any enterprise identity provider including Okta, Azure AD (Entra ID), Google Workspace, and OneLogin. Configuration is done through Settings in the workspace UI." },
|
||||
{ question: "How do I manage users when invitations are disabled?", answer: "Use the Admin API with your admin API key. You can create organizations, add members to organizations with specific roles, add users to workspaces with defined permissions, and remove users. All management is done through REST API calls authenticated with the x-admin-key header." },
|
||||
{ question: "Can I scale Sim horizontally for high availability?", answer: "The Docker Compose setup is designed for single-node deployments. For production scaling, you can deploy on Kubernetes with multiple application replicas behind a load balancer. The database can be scaled independently using managed PostgreSQL services. Redis can be configured for session and cache management across multiple instances." },
|
||||
{ question: "How do access control permission groups work?", answer: "Permission groups let you restrict which AI providers, workflow blocks, and platform features are available to specific team members. Users not assigned to any group have full access. Restrictions are enforced at both the UI level (hiding restricted options) and at execution time (blocking unauthorized operations). Enabling access control automatically enables organization management." },
|
||||
]} />
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"title": "Enterprise",
|
||||
"pages": ["index", "sso", "access-control", "whitelabeling", "audit-logs", "data-retention"],
|
||||
"defaultOpen": false
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
---
|
||||
title: Single Sign-On (SSO)
|
||||
description: Configure SAML 2.0 or OIDC-based single sign-on for your organization
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
import { Image } from '@/components/ui/image'
|
||||
|
||||
Single Sign-On lets your team sign in to Sim through your company's identity provider instead of managing separate passwords. Sim supports both OIDC and SAML 2.0.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Open SSO settings
|
||||
|
||||
Go to **Settings → Enterprise → Single Sign-On** in your workspace.
|
||||
|
||||
### 2. Choose a protocol
|
||||
|
||||
| Protocol | Use when |
|
||||
|----------|----------|
|
||||
| **OIDC** | Your IdP supports OpenID Connect — Okta, Microsoft Entra ID, Auth0, Google Workspace |
|
||||
| **SAML 2.0** | Your IdP is SAML-only — ADFS, Shibboleth, or older enterprise IdPs |
|
||||
|
||||
### 3. Fill in the form
|
||||
|
||||
<Image src="/static/enterprise/sso-form.png" alt="Single Sign-On configuration form showing Provider Type (OIDC), Provider ID, Issuer URL, Domain, Client ID, Client Secret, Scopes, and Callback URL fields" width={900} height={500} />
|
||||
|
||||
**Fields required for both protocols:**
|
||||
|
||||
| Field | What to enter |
|
||||
|-------|--------------|
|
||||
| **Provider ID** | A short slug identifying this connection, e.g. `okta` or `azure-ad`. Letters, numbers, and dashes only. |
|
||||
| **Issuer URL** | The identity provider's issuer URL. Must be HTTPS. |
|
||||
| **Domain** | Your organization's email domain, e.g. `company.com`. Users with this domain will be routed through SSO at sign-in. |
|
||||
|
||||
**OIDC additional fields:**
|
||||
|
||||
| Field | What to enter |
|
||||
|-------|--------------|
|
||||
| **Client ID** | The application client ID from your IdP. |
|
||||
| **Client Secret** | The client secret from your IdP. |
|
||||
| **Scopes** | Comma-separated OIDC scopes. Default: `openid,profile,email`. |
|
||||
|
||||
<Callout type="info">
|
||||
For OIDC, Sim automatically fetches endpoints (`authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`, `jwks_uri`) from your issuer's `/.well-known/openid-configuration` discovery document. You only need to provide the issuer URL.
|
||||
</Callout>
|
||||
|
||||
**SAML additional fields:**
|
||||
|
||||
| Field | What to enter |
|
||||
|-------|--------------|
|
||||
| **Entry Point URL** | The IdP's SSO service URL where Sim sends authentication requests. |
|
||||
| **Identity Provider Certificate** | The Base-64 encoded X.509 certificate from your IdP for verifying assertions. |
|
||||
|
||||
### 4. Copy the Callback URL
|
||||
|
||||
The **Callback URL** shown in the form is the endpoint your identity provider must redirect users back to after authentication. Copy it and register it in your IdP before saving.
|
||||
|
||||
**OIDC providers** (Okta, Microsoft Entra ID, Google Workspace, Auth0):
|
||||
```
|
||||
https://sim.ai/api/auth/sso/callback/{provider-id}
|
||||
```
|
||||
|
||||
**SAML providers** (ADFS, Shibboleth):
|
||||
```
|
||||
https://sim.ai/api/auth/sso/saml2/callback/{provider-id}
|
||||
```
|
||||
|
||||
### 5. Save and test
|
||||
|
||||
Click **Save**. To test, sign out and use the **Sign in with SSO** button on the login page. Enter an email address at your configured domain — Sim will redirect you to your identity provider.
|
||||
|
||||
---
|
||||
|
||||
## Provider Guides
|
||||
|
||||
<Tabs items={['Okta', 'Microsoft Entra ID', 'Google Workspace', 'ADFS']}>
|
||||
|
||||
<Tab value="Okta">
|
||||
|
||||
### Okta (OIDC)
|
||||
|
||||
**In Okta** ([official docs](https://help.okta.com/en-us/content/topics/apps/apps_app_integration_wizard_oidc.htm)):
|
||||
|
||||
1. Go to **Applications → Create App Integration**
|
||||
2. Select **OIDC - OpenID Connect**, then **Web Application**
|
||||
3. Set the **Sign-in redirect URI** to your Sim callback URL:
|
||||
```
|
||||
https://sim.ai/api/auth/sso/callback/okta
|
||||
```
|
||||
4. Under **Assignments**, grant access to the relevant users or groups
|
||||
5. Copy the **Client ID** and **Client Secret** from the app's **General** tab
|
||||
6. Your Okta domain is the hostname of your admin console, e.g. `dev-1234567.okta.com`
|
||||
|
||||
**In Sim:**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Provider Type | OIDC |
|
||||
| Provider ID | `okta` |
|
||||
| Issuer URL | `https://dev-1234567.okta.com/oauth2/default` |
|
||||
| Domain | `company.com` |
|
||||
| Client ID | From Okta app |
|
||||
| Client Secret | From Okta app |
|
||||
|
||||
The issuer URL uses Okta's default authorization server, which is pre-configured on every Okta org. If you created a custom authorization server, replace `default` with your server name.
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab value="Microsoft Entra ID">
|
||||
|
||||
### Microsoft Entra ID (OIDC)
|
||||
|
||||
**In Azure** ([official docs](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)):
|
||||
|
||||
1. Go to **Microsoft Entra ID → App registrations → New registration**
|
||||
2. Under **Redirect URI**, select **Web** and enter your Sim callback URL:
|
||||
```
|
||||
https://sim.ai/api/auth/sso/callback/azure-ad
|
||||
```
|
||||
3. After registration, go to **Certificates & secrets → New client secret** and copy the value immediately — it won't be shown again
|
||||
4. Go to **Overview** and copy the **Application (client) ID** and **Directory (tenant) ID**
|
||||
|
||||
**In Sim:**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Provider Type | OIDC |
|
||||
| Provider ID | `azure-ad` |
|
||||
| Issuer URL | `https://login.microsoftonline.com/{tenant-id}/v2.0` |
|
||||
| Domain | `company.com` |
|
||||
| Client ID | Application (client) ID |
|
||||
| Client Secret | Secret value |
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab value="Google Workspace">
|
||||
|
||||
### Google Workspace (OIDC)
|
||||
|
||||
**In Google Cloud Console** ([official docs](https://developers.google.com/identity/openid-connect/openid-connect)):
|
||||
|
||||
1. Go to **APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID**
|
||||
2. Set the application type to **Web application**
|
||||
3. Add your Sim callback URL to **Authorized redirect URIs**:
|
||||
```
|
||||
https://sim.ai/api/auth/sso/callback/google-workspace
|
||||
```
|
||||
4. Copy the **Client ID** and **Client Secret**
|
||||
|
||||
**In Sim:**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Provider Type | OIDC |
|
||||
| Provider ID | `google-workspace` |
|
||||
| Issuer URL | `https://accounts.google.com` |
|
||||
| Domain | `company.com` |
|
||||
| Client ID | From Google Cloud Console |
|
||||
| Client Secret | From Google Cloud Console |
|
||||
|
||||
<Callout type="info">
|
||||
To restrict sign-in to your Google Workspace domain, configure the OAuth consent screen and ensure your app is set to **Internal** (Workspace users only) under **User type**. Setting the app to Internal limits access to users within your Google Workspace organization.
|
||||
</Callout>
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab value="ADFS">
|
||||
|
||||
### ADFS (SAML 2.0)
|
||||
|
||||
**In ADFS** ([official docs](https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-relying-party-trust)):
|
||||
|
||||
1. Open **AD FS Management → Relying Party Trusts → Add Relying Party Trust**
|
||||
2. Choose **Claims aware**, then **Enter data about the relying party manually**
|
||||
3. Set the **Relying party identifier** (Entity ID) to your Sim base URL:
|
||||
```
|
||||
https://sim.ai
|
||||
```
|
||||
4. Add an endpoint: **SAML Assertion Consumer Service** (HTTP POST) with the URL:
|
||||
```
|
||||
https://sim.ai/api/auth/sso/saml2/callback/adfs
|
||||
```
|
||||
5. Export the **Token-signing certificate** from **Certificates**: right-click → **View Certificate → Details → Copy to File**, choose **Base-64 encoded X.509 (.CER)**. The `.cer` file is PEM-encoded — rename it to `.pem` before pasting its contents into Sim.
|
||||
6. Note the **ADFS Federation Service endpoint URL** (e.g. `https://adfs.company.com/adfs/ls`)
|
||||
|
||||
**In Sim:**
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Provider Type | SAML |
|
||||
| Provider ID | `adfs` |
|
||||
| Issuer URL | `https://sim.ai` |
|
||||
| Domain | `company.com` |
|
||||
| Entry Point URL | `https://adfs.company.com/adfs/ls` |
|
||||
| Certificate | Contents of the `.pem` file |
|
||||
|
||||
<Callout type="info">
|
||||
For ADFS, the **Issuer URL** field is the SP entity ID — the identifier ADFS uses to identify Sim as a relying party. It must match the **Relying party identifier** you registered in ADFS.
|
||||
</Callout>
|
||||
|
||||
</Tab>
|
||||
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## How sign-in works after setup
|
||||
|
||||
Once SSO is configured, users with your domain (`company.com`) can sign in through your identity provider:
|
||||
|
||||
1. User goes to `sim.ai` and clicks **Sign in with SSO**
|
||||
2. They enter their work email (e.g. `alice@company.com`)
|
||||
3. Sim redirects them to your identity provider
|
||||
4. After authenticating, they are returned to Sim and added to your organization automatically
|
||||
5. They land in the workspace
|
||||
|
||||
Users who sign in via SSO for the first time are automatically provisioned and added to your organization — no manual invite required.
|
||||
|
||||
<Callout type="info">
|
||||
Password-based login remains available. Forcing all organization members to use SSO exclusively is not yet supported.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{
|
||||
question: "Which SSO providers are supported?",
|
||||
answer: "Any identity provider that supports OIDC or SAML 2.0. This includes Okta, Microsoft Entra ID (Azure AD), Google Workspace, Auth0, OneLogin, JumpCloud, Ping Identity, ADFS, Shibboleth, and more."
|
||||
},
|
||||
{
|
||||
question: "What is the Domain field used for?",
|
||||
answer: "The domain (e.g. company.com) is how Sim routes users to the right identity provider. When a user enters their email on the SSO sign-in page, Sim matches their email domain to a registered SSO provider and redirects them there."
|
||||
},
|
||||
{
|
||||
question: "Do I need to provide OIDC endpoints manually?",
|
||||
answer: "No. For OIDC providers, Sim automatically fetches the authorization, token, and JWKS endpoints from the discovery document at {issuer}/.well-known/openid-configuration. You only need to provide the issuer URL."
|
||||
},
|
||||
{
|
||||
question: "What happens when a user signs in with SSO for the first time?",
|
||||
answer: "Sim creates an account for them automatically and adds them to your organization. No manual invite is needed. They are assigned the member role by default."
|
||||
},
|
||||
{
|
||||
question: "Can I still use email/password login after enabling SSO?",
|
||||
answer: "Yes. Enabling SSO does not disable password-based login. Users can still sign in with their email and password if they have one. Forced SSO (requiring all users on the domain to use SSO) is not yet supported."
|
||||
},
|
||||
{
|
||||
question: "Who can configure SSO on Sim Cloud?",
|
||||
answer: "Organization owners and admins can configure SSO. You must be on the Enterprise plan."
|
||||
},
|
||||
{
|
||||
question: "What is the Callback URL?",
|
||||
answer: "The Callback URL (also called Redirect URI or ACS URL) is the endpoint in Sim that receives the authentication response from your identity provider. For OIDC providers it follows the format: https://sim.ai/api/auth/sso/callback/{provider-id}. For SAML providers it is: https://sim.ai/api/auth/sso/saml2/callback/{provider-id}. You must register this URL in your identity provider before SSO will work."
|
||||
},
|
||||
{
|
||||
question: "How do I update or replace an existing SSO configuration?",
|
||||
answer: "Open Settings → Enterprise → Single Sign-On and click Edit. Update the fields and save. The existing provider configuration is replaced."
|
||||
}
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
Self-hosted deployments use environment variables instead of the billing/plan check.
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
# Required
|
||||
SSO_ENABLED=true
|
||||
NEXT_PUBLIC_SSO_ENABLED=true
|
||||
|
||||
# Required if you want users auto-added to your organization on first SSO sign-in
|
||||
ORGANIZATIONS_ENABLED=true
|
||||
NEXT_PUBLIC_ORGANIZATIONS_ENABLED=true
|
||||
```
|
||||
|
||||
You can register providers through the **Settings UI** (same as cloud) or by running the registration script directly against your database.
|
||||
|
||||
### Script-based registration
|
||||
|
||||
Use this when you need to register an SSO provider without going through the UI — for example, during initial deployment or CI/CD automation.
|
||||
|
||||
```bash
|
||||
# OIDC example (Okta)
|
||||
SSO_ENABLED=true \
|
||||
NEXT_PUBLIC_APP_URL=https://your-instance.com \
|
||||
SSO_PROVIDER_TYPE=oidc \
|
||||
SSO_PROVIDER_ID=okta \
|
||||
SSO_ISSUER=https://dev-1234567.okta.com/oauth2/default \
|
||||
SSO_DOMAIN=company.com \
|
||||
SSO_USER_EMAIL=admin@company.com \
|
||||
SSO_OIDC_CLIENT_ID=your-client-id \
|
||||
SSO_OIDC_CLIENT_SECRET=your-client-secret \
|
||||
bun run packages/db/scripts/register-sso-provider.ts
|
||||
```
|
||||
|
||||
```bash
|
||||
# SAML example (ADFS)
|
||||
SSO_ENABLED=true \
|
||||
NEXT_PUBLIC_APP_URL=https://your-instance.com \
|
||||
SSO_PROVIDER_TYPE=saml \
|
||||
SSO_PROVIDER_ID=adfs \
|
||||
SSO_ISSUER=https://your-instance.com \
|
||||
SSO_DOMAIN=company.com \
|
||||
SSO_USER_EMAIL=admin@company.com \
|
||||
SSO_SAML_ENTRY_POINT=https://adfs.company.com/adfs/ls \
|
||||
SSO_SAML_CERT="-----BEGIN CERTIFICATE-----
|
||||
...
|
||||
-----END CERTIFICATE-----" \
|
||||
bun run packages/db/scripts/register-sso-provider.ts
|
||||
```
|
||||
|
||||
The script outputs the callback URL to configure in your IdP once it completes.
|
||||
|
||||
To remove a provider:
|
||||
|
||||
```bash
|
||||
SSO_USER_EMAIL=admin@company.com \
|
||||
bun run packages/db/scripts/deregister-sso-provider.ts
|
||||
```
|
||||
@@ -1,103 +0,0 @@
|
||||
---
|
||||
title: Whitelabeling
|
||||
description: Replace Sim branding with your own logo, colors, and links
|
||||
---
|
||||
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
import { Image } from '@/components/ui/image'
|
||||
|
||||
Whitelabeling lets you replace Sim's default branding — logo, colors, and support links — with your own. Members of your organization see your brand instead of Sim's throughout the workspace.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Open Whitelabeling settings
|
||||
|
||||
Go to **Settings → Enterprise → Whitelabeling** in your workspace.
|
||||
|
||||
<Image src="/static/enterprise/whitelabeling.png" alt="Whitelabeling settings showing brand identity fields (Logo, Wordmark, Brand name), color pickers for primary and accent colors, and link fields for support email and documentation URL" width={900} height={500} />
|
||||
|
||||
### 2. Configure brand identity
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Logo** | Shown in the collapsed sidebar. Square image (PNG, JPEG, SVG, or WebP). Max 5 MB. |
|
||||
| **Wordmark** | Shown in the expanded sidebar. Wide image (PNG, JPEG, SVG, or WebP). Max 5 MB. |
|
||||
| **Brand name** | Replaces "Sim" in the sidebar and select UI elements. Max 64 characters. |
|
||||
|
||||
|
||||
### 3. Configure colors
|
||||
|
||||
All colors must be valid hex values (e.g. `#701ffc`).
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Primary color** | Main accent color used for buttons and active states. |
|
||||
| **Primary hover color** | Color shown when hovering over primary elements. |
|
||||
| **Accent color** | Secondary accent for highlights and secondary interactive elements. |
|
||||
| **Accent hover color** | Color shown when hovering over accent elements. |
|
||||
|
||||
### 4. Configure links
|
||||
|
||||
Replace Sim's default support and legal links with your own.
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Support email** | Shown in help prompts. Must be a valid email address. |
|
||||
| **Documentation URL** | Link to your internal documentation. Must be a valid URL. |
|
||||
| **Terms of service URL** | Link to your terms page. Must be a valid URL. |
|
||||
| **Privacy policy URL** | Link to your privacy page. Must be a valid URL. |
|
||||
|
||||
### 5. Save
|
||||
|
||||
Click **Save changes**. The new branding is applied immediately for all members of your organization.
|
||||
|
||||
---
|
||||
|
||||
## What gets replaced
|
||||
|
||||
Whitelabeling replaces the following visual elements:
|
||||
|
||||
- **Sidebar logo and wordmark** — your uploaded images replace the Sim logo
|
||||
- **Brand name** — appears in the sidebar and select UI labels
|
||||
- **Primary and accent colors** — applied to buttons, active states, and highlights
|
||||
- **Support and legal links** — help prompts and footer links point to your URLs
|
||||
|
||||
Whitelabeling applies only to members of your organization. Public-facing pages (login, marketing) are not affected.
|
||||
|
||||
---
|
||||
|
||||
<FAQ items={[
|
||||
{
|
||||
question: "Who can configure whitelabeling?",
|
||||
answer: "Organization owners and admins can configure whitelabeling. On Sim Cloud, you must be on the Enterprise plan."
|
||||
},
|
||||
{
|
||||
question: "What image formats are supported?",
|
||||
answer: "PNG, JPEG, SVG, and WebP. Maximum file size is 5 MB for both the logo and wordmark."
|
||||
},
|
||||
{
|
||||
question: "What is the difference between the logo and the wordmark?",
|
||||
answer: "The logo is a square image shown in the collapsed sidebar. The wordmark is a wide image shown in the expanded sidebar alongside member names and navigation items."
|
||||
},
|
||||
{
|
||||
question: "Do members outside my organization see the custom branding?",
|
||||
answer: "No. Custom branding is scoped to your organization. Members see your branding when signed in to your organization's workspace."
|
||||
}
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Self-hosted setup
|
||||
|
||||
Self-hosted deployments use environment variables instead of the billing/plan check.
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
WHITELABELING_ENABLED=true
|
||||
NEXT_PUBLIC_WHITELABELING_ENABLED=true
|
||||
```
|
||||
|
||||
Once enabled, configure branding through **Settings → Enterprise → Whitelabeling** the same way.
|
||||
@@ -308,17 +308,6 @@ By default, your usage is capped at the credits included in your plan. To allow
|
||||
|
||||
## Plan Limits
|
||||
|
||||
### Workspaces
|
||||
|
||||
| Plan | Personal Workspaces | Shared (Organization) Workspaces |
|
||||
|------|---------------------|----------------------------------|
|
||||
| **Free** | 1 | — |
|
||||
| **Pro** | Up to 3 | — |
|
||||
| **Max** | Up to 10 | — |
|
||||
| **Team / Enterprise** | Unlimited | Unlimited |
|
||||
|
||||
Team and Enterprise plans unlock shared workspaces that belong to your organization. Members invited to a shared workspace automatically join the organization and count toward your seat total. When a Team or Enterprise subscription is cancelled or downgraded, existing shared workspaces remain accessible to current members but new invites are disabled until the organization is upgraded again.
|
||||
|
||||
### Rate Limits
|
||||
|
||||
| Plan | Sync (req/min) | Async (req/min) |
|
||||
@@ -378,12 +367,12 @@ Sim uses a **base subscription + overage** billing model:
|
||||
|
||||
### Threshold Billing
|
||||
|
||||
When on-demand is enabled and unbilled overage reaches $100, Sim automatically bills the full unbilled amount.
|
||||
When on-demand is enabled and unbilled overage reaches $50, Sim automatically bills the full unbilled amount.
|
||||
|
||||
**Example:**
|
||||
- Day 10: $120 overage → Bill $120 immediately
|
||||
- Day 15: Additional $60 usage ($180 total) → Already billed, no action
|
||||
- Day 20: Another $80 usage ($260 total, $140 unbilled) → Bill $140 immediately
|
||||
- Day 10: $70 overage → Bill $70 immediately
|
||||
- Day 15: Additional $35 usage ($105 total) → Already billed, no action
|
||||
- Day 20: Another $50 usage ($155 total, $85 unbilled) → Bill $85 immediately
|
||||
|
||||
This spreads large overage charges throughout the month instead of one large bill at period end.
|
||||
|
||||
@@ -491,5 +480,5 @@ import { FAQ } from '@/components/ui/faq'
|
||||
{ question: "What happens when I exceed my plan's credit limit?", answer: "By default, your usage is capped at your plan's included credits and runs will stop. If you enable on-demand billing or manually raise your usage limit in Settings, you can continue running workflows and pay for the overage at the end of the billing period." },
|
||||
{ question: "How does the 1.1x hosted model multiplier work?", answer: "When you use Sim's hosted API keys (instead of bringing your own), a 1.1x multiplier is applied to the base model pricing for Agent blocks. This covers infrastructure and API management costs. You can avoid this multiplier by using your own API keys via the BYOK feature." },
|
||||
{ question: "Are there any free options for AI models?", answer: "Yes. If you run local models through Ollama or VLLM, there are no API costs for those model calls. You still pay the base run charge of 1 credit per run." },
|
||||
{ question: "When does threshold billing trigger?", answer: "When on-demand billing is enabled and your unbilled overage reaches $100, Sim automatically bills the full unbilled amount. This spreads large charges throughout the month instead of accumulating one large bill at period end." },
|
||||
{ question: "When does threshold billing trigger?", answer: "When on-demand billing is enabled and your unbilled overage reaches $50, Sim automatically bills the full unbilled amount. This spreads large charges throughout the month instead of accumulating one large bill at period end." },
|
||||
]} />
|
||||
|
||||
@@ -220,6 +220,6 @@ import { FAQ } from '@/components/ui/faq'
|
||||
{ question: "Who can configure MCP servers in a workspace?", answer: "Users with Write permission can configure (add and update) MCP servers in workspace settings. Only Admin permission is required to delete MCP servers. Users with Read permission can view available MCP tools and execute them in agents and MCP Tool blocks. This means all workspace members with at least Read access can use MCP tools in their workflows." },
|
||||
{ question: "Can I use MCP servers from multiple workspaces?", answer: "MCP servers are configured per workspace. Each workspace maintains its own set of MCP server connections. If you need the same MCP server in multiple workspaces, you need to configure it separately in each workspace's settings." },
|
||||
{ question: "How do I update MCP tool schemas after a server changes its available tools?", answer: "Click the Refresh button on the MCP server in your workspace settings. This fetches the latest tool schemas from the server and automatically updates any agent blocks that use those tools with the new parameter definitions." },
|
||||
{ question: "Can permission groups restrict access to MCP tools?", answer: "Yes. On Enterprise-entitled workspaces, any workspace admin can create a permission group that disables MCP tools for its members using the disableMcpTools option. When this is enabled, affected users will not be able to add or use MCP tools in workflows that belong to that workspace." },
|
||||
{ question: "Can permission groups restrict access to MCP tools?", answer: "Yes. Organization admins can create permission groups that disable MCP tools for specific members using the disableMcpTools configuration option. When this is enabled, affected users will not be able to add or use MCP tools in their workflows." },
|
||||
{ question: "What happens if an MCP server goes offline during workflow execution?", answer: "If the MCP server is unreachable during execution, the tool call will fail and return an error. In an Agent block, the AI may attempt to handle the failure gracefully. In a standalone MCP Tool block, the workflow step will fail. Check MCP server logs and verify the server is running and accessible to troubleshoot connectivity issues." },
|
||||
]} />
|
||||
@@ -25,7 +25,7 @@
|
||||
"execution",
|
||||
"permissions",
|
||||
"self-hosting",
|
||||
"enterprise",
|
||||
"./enterprise/index",
|
||||
"./keyboard-shortcuts/index"
|
||||
],
|
||||
"defaultOpen": false
|
||||
|
||||
@@ -4,17 +4,18 @@ description: Upload, create, edit, and generate files — documents, presentatio
|
||||
---
|
||||
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/files-pipeline-deals-summarizer.mp4" width={700} height={450} />
|
||||
|
||||
Describe a document, presentation, image, or visualization and Mothership creates it — streaming the content live into the resource panel as it writes. Attach any file to your message and Mothership reads it, processes it, and saves it to your workspace.
|
||||
|
||||
{/* TODO: Screenshot of Mothership with the File Write subagent active — file content streaming into the resource panel in split or preview mode. Shows the live streaming preview experience as a document is being written. */}
|
||||
|
||||
## Uploading Files to the Workspace
|
||||
|
||||
Attach any file directly to your Mothership message — drag it into the input, paste it, or click the attachment icon. Mothership reads the file as context and saves it to your workspace.
|
||||
|
||||
{/* TODO: Screenshot of the Mothership input area showing a file attached — e.g., a PDF or image thumbnail visible in the input before sending. */}
|
||||
|
||||
Use this to:
|
||||
- Hand Mothership a document and ask it to process, summarize, or extract data from it
|
||||
- Upload a CSV and have it create a table from it
|
||||
@@ -47,8 +48,6 @@ Open a file using `@filename` or the **+** menu, then describe the change:
|
||||
|
||||
## Presentations
|
||||
|
||||
<Image src="/static/mothership/pptx-example.png" alt="Mothership resource panel showing a generated Mothership-Use-Cases.pptx file open with the title slide and first use case slide visible" width={900} height={500} />
|
||||
|
||||
Mothership can generate `.pptx` files:
|
||||
|
||||
- "Create a pitch deck for Q3 review — 8 slides covering growth, retention, and roadmap"
|
||||
@@ -59,6 +58,8 @@ Mothership can generate `.pptx` files:
|
||||
|
||||
The file is saved to your workspace and can be downloaded.
|
||||
|
||||
{/* TODO: Screenshot of the resource panel with a generated .pptx file open or a download prompt visible, showing the file name and confirming it was saved to the workspace. */}
|
||||
|
||||
## Images
|
||||
|
||||
Mothership can generate images using AI, and can use an existing image as a reference to guide the output:
|
||||
@@ -72,7 +73,7 @@ Mothership can generate images using AI, and can use an existing image as a refe
|
||||
- Attach an existing image to your message, then describe what you want: "Generate a new version of this banner with a blue color scheme instead of green"
|
||||
- "Create a variation of this diagram with the boxes rearranged horizontally [attach image]"
|
||||
|
||||
<Image src="/static/mothership/image-example.png" alt="Mothership resource panel showing a generated hero image of a Mothership-branded blimp flying over San Francisco at golden hour, alongside the chat response linking the file" width={900} height={500} />
|
||||
{/* TODO: Screenshot of the resource panel showing a generated image open as a file tab — ideally with the image rendered in the viewer panel. */}
|
||||
|
||||
Generated images are saved as workspace files.
|
||||
|
||||
@@ -84,7 +85,7 @@ Mothership can generate charts and data visualizations from data you describe or
|
||||
- "Create a line chart of token usage over the past 30 days from this data [paste data]"
|
||||
- "Generate a pie chart showing the distribution of lead sources from the leads table"
|
||||
|
||||
<Image src="/static/mothership/chart-example.png" alt="Mothership resource panel showing a generated chart file with bar charts for backend 5xx errors and error rate over time" width={900} height={500} />
|
||||
{/* TODO: Screenshot of a chart or visualization rendered in the resource panel as a file. */}
|
||||
|
||||
Visualizations are saved as files and rendered in the resource panel.
|
||||
|
||||
@@ -103,7 +104,7 @@ Results come back directly in the chat. Ask Mothership to save the output as a f
|
||||
|
||||
When a file opens in the resource panel, you can switch between three views:
|
||||
|
||||
<Video src="mothership/toggle-file-view.mp4" width={700} height={450} />
|
||||
{/* TODO: Screenshot of the file viewer in the resource panel showing the mode selector (editor/split/preview), ideally in split mode with a markdown file showing raw content on the left and rendered preview on the right. */}
|
||||
|
||||
| Mode | What it shows |
|
||||
|------|--------------|
|
||||
|
||||
@@ -4,13 +4,12 @@ description: Your AI command center. Build and manage your entire workspace in n
|
||||
---
|
||||
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/create-workflow.mp4" width={700} height={450} />
|
||||
|
||||
Describe what you want and Mothership handles it. Build a workflow, run research, generate a presentation, query a table, schedule a recurring job, send a Slack message — Mothership knows your entire workspace and takes action directly.
|
||||
|
||||
{/* TODO: Screenshot or GIF of the full Mothership home page — chat pane on the left with a conversation in progress, resource panel on the right with a workflow or file tab open. Hero shot for the page. */}
|
||||
|
||||
## What You Can Do
|
||||
|
||||
| Area | What Mothership can do |
|
||||
@@ -45,8 +44,6 @@ For complex tasks, Mothership delegates to specialized subagents automatically.
|
||||
|
||||
Bring any workspace object into the conversation via the **+** menu, `@`-mentions, or drag-and-drop from the sidebar. Mothership also opens resources automatically when it creates or modifies them.
|
||||
|
||||
<Video src="mothership/context-menu.mp4" width={700} height={450} />
|
||||
|
||||
{/* TODO: Screenshot of the resource panel with multiple tabs open — a workflow tab, a table tab, and a file tab — showing different resource types side by side. */}
|
||||
|
||||
| What to add | How it appears |
|
||||
@@ -62,8 +59,6 @@ Bring any workspace object into the conversation via the **+** menu, `@`-mention
|
||||
|
||||
Mothership has two panes. On the left: the chat thread, where your messages and Mothership's responses appear. On the right: the resource panel, where workflows, tables, files, and knowledge bases open as tabs. The panel is resizable; tabs are draggable and closeable.
|
||||
|
||||
<Video src="mothership/split-view.mp4" width={700} height={450} />
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "How is Mothership different from Copilot?", answer: "Copilot is scoped to a single workflow — it helps you build and edit that workflow. Mothership has access to your entire workspace and can build workflows, manage data, run research, schedule jobs, take actions across integrations, and more." },
|
||||
{ question: "What model does Mothership use?", answer: "Mothership always uses Claude Opus 4.6. There is no model selector." },
|
||||
|
||||
@@ -4,13 +4,12 @@ description: Create, populate, and query knowledge bases from Mothership.
|
||||
---
|
||||
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/kb.mp4" width={700} height={450} />
|
||||
|
||||
Create a knowledge base, add documents to it, and query it in plain language — all through conversation. Knowledge bases you create in Mothership are immediately available to Agent blocks in any workflow.
|
||||
|
||||
{/* TODO: Screenshot of Mothership with a knowledge base open in the resource panel — showing the knowledge base name, document list, and status of indexed documents. */}
|
||||
|
||||
## Creating Knowledge Bases
|
||||
|
||||
Describe the knowledge base and Mothership creates it:
|
||||
|
||||
@@ -4,13 +4,12 @@ description: Ask Mothership to research anything — it searches, reads, and syn
|
||||
---
|
||||
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/research-agent.mp4" width={700} height={450} />
|
||||
|
||||
Ask Mothership to research anything and it figures out the best approach — searching the web, reading specific pages, crawling sites, looking up technical docs. Just describe what you want to know.
|
||||
|
||||
{/* TODO: Screenshot of the Research subagent section in the Mothership chat — expanded, showing it working through a research task with the final report or answer appearing. Ideally with a file tab open in the resource panel showing the output. */}
|
||||
|
||||
## Asking Questions
|
||||
|
||||
Ask anything — about a company, a competitor, a market, a technical question, or a specific URL:
|
||||
|
||||
@@ -6,9 +6,9 @@ description: Create, query, and manage workspace tables from Mothership.
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Image src="/static/mothership/table-example.png" alt="Mothership resource panel showing the pipeline_deals table with company, deal_owner, stage, and amount columns, alongside a chat summary of total pipeline value and breakdown by stage" width={900} height={500} />
|
||||
Create a table from a description or a CSV, query it in plain language, add or update rows, and export the results — all through conversation. Tables open in the resource panel when created or referenced.
|
||||
|
||||
Create a table from a description or a CSV, query it in plain language, add or update rows, and export the results — all through conversation. Tables open in the resource panel as soon as they're created or referenced.
|
||||
{/* TODO: Screenshot of Mothership with a table open in the resource panel — ideally after a query or row operation, showing the table with data populated. */}
|
||||
|
||||
## Creating Tables
|
||||
|
||||
|
||||
@@ -5,17 +5,16 @@ description: Schedule recurring jobs, take immediate actions, connect integratio
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/job-create.mp4" width={700} height={450} />
|
||||
|
||||
Mothership can act on your behalf right now — send a message, create an issue, call an API — or on a schedule, running a prompt automatically every hour, day, or week. It can also connect integrations, set environment variables, add MCP servers, and create custom tools.
|
||||
|
||||
## Scheduled Jobs
|
||||
|
||||
A scheduled job is a Mothership task that runs on a cron schedule. On each run, Mothership reads the current workspace state and executes the job's prompt as if you had just sent it.
|
||||
|
||||
{/* TODO: Screenshot of Mothership chat confirming a scheduled job was created — showing the job name, schedule, and what it will do. If there's a jobs list view in the sidebar, include that as a second screenshot here. */}
|
||||
|
||||
### Creating a Job
|
||||
|
||||
Describe the recurring task and how often it should run:
|
||||
|
||||
@@ -3,14 +3,14 @@ title: Workflows
|
||||
description: Create, edit, run, debug, deploy, and organize workflows from Mothership.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<Video src="mothership/create-workflow.mp4" width={700} height={450} />
|
||||
|
||||
Describe a workflow and Mothership builds it. Reference an existing one by name and it edits it. No canvas navigation required — every change appears in the resource panel in real time.
|
||||
|
||||
{/* TODO: Screenshot of Mothership chat on the left with the Build subagent section visible, and a workflow open in the resource panel on the right. Shows the split-pane experience of building via natural language. */}
|
||||
|
||||
## Creating Workflows
|
||||
|
||||
Describe what the workflow should do — what triggers it, what it should do, which integrations it needs, and what it should return. Mothership builds it and opens the canvas in the resource panel.
|
||||
@@ -33,7 +33,7 @@ Open an existing workflow with `@workflow-name` or the **+** menu, then describe
|
||||
|
||||
## Running Workflows
|
||||
|
||||
<Video src="mothership/run-workflow.mp4" width={700} height={450} />
|
||||
{/* TODO: Screenshot or GIF of Mothership running a workflow — showing the chat streaming execution output on the left while the workflow canvas in the resource panel highlights blocks as they execute in real time. */}
|
||||
|
||||
Ask Mothership to run a workflow and it handles the execution:
|
||||
|
||||
@@ -110,6 +110,10 @@ Variables set this way are available via `<variable.VARIABLE_NAME>` syntax insid
|
||||
- "Delete the old_api_prototype workflow"
|
||||
- "Delete all workflows in the deprecated folder"
|
||||
|
||||
<Callout type="warn">
|
||||
Workflow deletion is permanent. Deployed versions are also removed. There is no recycle bin.
|
||||
</Callout>
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "Can Mothership edit a workflow while it's deployed?", answer: "Yes. Editing a workflow does not affect the live deployment. The deployed version is a snapshot — you need to ask Mothership to redeploy to push changes to production." },
|
||||
{ question: "Can I run a workflow with specific inputs from Mothership?", answer: "Yes. Describe the inputs in your message and Mothership passes them to the workflow's start block." },
|
||||
|
||||
@@ -2,31 +2,10 @@
|
||||
title: "Roles and Permissions"
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Video } from '@/components/ui/video'
|
||||
|
||||
When you invite team members to your organization or workspace, you'll need to choose what level of access to give them. This guide explains what each permission level allows users to do, helping you understand team roles and what access each permission level provides.
|
||||
|
||||
## Workspaces and Organizations
|
||||
|
||||
Sim has two kinds of workspaces:
|
||||
|
||||
- **Personal workspaces** live under your individual account. The number you can create depends on your plan.
|
||||
- **Shared (organization) workspaces** live under an organization and are available on Team and Enterprise plans. Any organization Owner or Admin can create them. Members invited to a shared workspace automatically join the organization and count toward your seat total.
|
||||
|
||||
### Workspace Limits by Plan
|
||||
|
||||
| Plan | Personal Workspaces | Shared Workspaces |
|
||||
|------|---------------------|-------------------|
|
||||
| **Free** | 1 | — |
|
||||
| **Pro** | Up to 3 | — |
|
||||
| **Max** | Up to 10 | — |
|
||||
| **Team / Enterprise** | Unlimited | Unlimited (seat-gated invites) |
|
||||
|
||||
<Callout type="info">
|
||||
When a Team or Enterprise subscription is cancelled or downgraded, existing shared workspaces stay accessible to current members. New invitations are blocked until the organization is upgraded again.
|
||||
</Callout>
|
||||
|
||||
## How to Invite Someone to a Workspace
|
||||
|
||||
<div className="mx-auto w-full overflow-hidden rounded-lg">
|
||||
@@ -109,10 +88,6 @@ Every workspace has one **Owner** (the person who created it) plus any number of
|
||||
- Can do everything except delete the workspace or remove the owner
|
||||
- Can be removed from the workspace by the owner or other admins
|
||||
|
||||
<Callout type="info">
|
||||
For shared (organization) workspaces, the organization's Owner and Admins are treated as Admins of every workspace in the organization, even without an explicit per-workspace invite.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Common Scenarios
|
||||
@@ -170,41 +145,28 @@ Periodically review who has access to what, especially when team members change
|
||||
|
||||
## Organization Roles
|
||||
|
||||
An organization has three roles: **Owner**, **Admin**, and **Member**.
|
||||
|
||||
### Organization Owner
|
||||
**What they can do:**
|
||||
- Everything an Admin can do
|
||||
- Transfer organization ownership to another user
|
||||
- Only one Owner exists per organization
|
||||
When inviting someone to your organization, you can assign one of two roles:
|
||||
|
||||
### Organization Admin
|
||||
**What they can do:**
|
||||
- Invite and remove team members from the organization
|
||||
- Create new shared workspaces under the organization
|
||||
- Manage billing, seat count, and subscription settings
|
||||
- Access all shared workspaces within the organization as a workspace Admin
|
||||
- Promote members to Admin or demote Admins to Member
|
||||
|
||||
<Callout type="info">
|
||||
Owners and Admins have the same day-to-day permissions. The only action reserved for the Owner is transferring ownership.
|
||||
</Callout>
|
||||
- Create new workspaces
|
||||
- Manage billing and subscription settings
|
||||
- Access all workspaces within the organization
|
||||
|
||||
### Organization Member
|
||||
**What they can do:**
|
||||
- Access shared workspaces they've been specifically invited to
|
||||
- Access workspaces they've been specifically invited to
|
||||
- View the list of organization members
|
||||
- Cannot invite new people, create shared workspaces, or manage organization settings
|
||||
- Cannot invite new people or manage organization settings
|
||||
|
||||
import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "What is the difference between organization roles and workspace permissions?", answer: "Organization roles (Owner, Admin, or Member) control who can manage the organization itself, including inviting people, creating shared workspaces, and handling billing. Workspace permissions (Read, Write, Admin) control what a user can do within a specific workspace, such as viewing, editing, or managing workflows. A user needs both an organization role and a workspace permission to work within a shared workspace." },
|
||||
{ question: "How many workspaces can I create?", answer: "Free users get 1 personal workspace. Pro users get up to 3 personal workspaces. Max users get up to 10 personal workspaces. Team and Enterprise plans support unlimited shared workspaces under the organization — new invites are gated by your seat count." },
|
||||
{ question: "What happens to my shared workspaces if I cancel or downgrade my Team plan?", answer: "Existing shared workspaces remain accessible to current members, but new invitations are disabled until you upgrade back to a Team or Enterprise plan. No workspaces or members are deleted — the organization is simply dormant until billing is re-enabled." },
|
||||
{ question: "Can I restrict which integrations or model providers a team member can use?", answer: "Yes, on Enterprise-entitled workspaces. Any workspace admin can create permission groups with fine-grained controls, including restricting allowed integrations and allowed model providers to specific lists. You can also disable access to MCP tools, custom tools, skills, and various platform features like the knowledge base, API keys, or Copilot on a per-group basis. Permission groups are scoped per workspace — a user can belong to different groups in different workspaces." },
|
||||
{ question: "What is the difference between organization roles and workspace permissions?", answer: "Organization roles (Admin or Member) control who can manage the organization itself, including inviting people, creating workspaces, and handling billing. Workspace permissions (Read, Write, Admin) control what a user can do within a specific workspace, such as viewing, editing, or managing workflows. A user needs both an organization role and a workspace permission to work within a workspace." },
|
||||
{ question: "Can I restrict which integrations or model providers a team member can use?", answer: "Yes. Organization admins can create permission groups with fine-grained controls, including restricting allowed integrations and allowed model providers to specific lists. You can also disable access to MCP tools, custom tools, skills, and various platform features like the knowledge base, API keys, or Copilot on a per-group basis." },
|
||||
{ question: "What happens when a personal environment variable has the same name as a workspace variable?", answer: "The personal environment variable takes priority. When a workflow runs, if both a personal and workspace variable share the same name, the personal value is used. This allows individual users to override shared workspace configuration when needed." },
|
||||
{ question: "Can an Admin remove the workspace owner?", answer: "No. The workspace owner cannot be removed from the workspace by anyone. Only the workspace owner can delete the workspace or transfer ownership to another user. Admins can do everything else, including inviting and removing other users and managing workspace settings." },
|
||||
{ question: "What are permission groups and how do they work?", answer: "Permission groups are an Enterprise access control feature that lets workspace admins define granular restrictions beyond the standard Read/Write/Admin roles. Groups are scoped to a single workspace: each user can be in at most one group per workspace, and a user can be in different groups across different workspaces. A permission group can hide UI sections (like trace spans, knowledge base, API keys, or deployment options), disable features (MCP tools, custom tools, skills, invitations), and restrict which integrations and model providers its members can access. Members can be assigned manually, and new members can be auto-added on join. Execution-time enforcement is based on the workflow's workspace, not the user's current UI context." },
|
||||
{ question: "What are permission groups and how do they work?", answer: "Permission groups are an advanced access control feature that lets organization admins define granular restrictions beyond the standard Read/Write/Admin roles. A permission group can hide UI sections (like trace spans, knowledge base, API keys, or deployment options), disable features (MCP tools, custom tools, skills, invitations), and restrict which integrations and model providers members can access. Members can be assigned to groups, and new members can be auto-added." },
|
||||
{ question: "How should I set up permissions for a new team member?", answer: "Start with the lowest permission level they need. Invite them to the organization as a Member, then add them to the relevant workspace with Read permission if they only need visibility, Write if they need to create and run workflows, or Admin if they need to manage the workspace and its users. You can always increase permissions later." },
|
||||
]} />
|
||||
@@ -140,7 +140,7 @@ import { FAQ } from '@/components/ui/faq'
|
||||
{ question: "How does the agent decide when to load a skill?", answer: "The agent sees an available_skills section in its system prompt listing each skill's name and description. When the agent determines that a skill is relevant to the current task, it calls the load_skill tool with the skill name. The full skill content is then returned as a tool response. This is why writing a specific, keyword-rich description is critical -- it is the only thing the agent reads before deciding whether to activate a skill." },
|
||||
{ question: "Do skills work with all LLM providers?", answer: "Yes. The load_skill mechanism uses standard tool-calling, which is supported by all LLM providers in Sim. No provider-specific configuration is needed. The skill system works the same way whether you are using Anthropic, OpenAI, Google, or any other supported provider." },
|
||||
{ question: "When should I use skills vs. agent instructions?", answer: "Use skills for knowledge that applies across multiple workflows or changes frequently. Skills are reusable packages that can be attached to any agent. Use agent instructions for task-specific context that is unique to a single agent and workflow. If you find yourself copying the same instructions into multiple agents, that content should be a skill instead." },
|
||||
{ question: "Can permission groups disable skills for certain users?", answer: "Yes. On Enterprise-entitled workspaces, any workspace admin can create a permission group with the disableSkills option enabled. When a user is assigned to such a group in a workspace, the skills dropdown in agent blocks is disabled and they cannot add or use skills in workflows belonging to that workspace." },
|
||||
{ question: "Can permission groups disable skills for certain users?", answer: "Yes. Organization admins can create permission groups with the disableSkills option enabled. When a user is assigned to such a permission group, the skills dropdown in agent blocks will be disabled and they will not be able to add or use skills in their workflows." },
|
||||
{ question: "What is the recommended maximum length for skill content?", answer: "Keep skills focused and under 500 lines. If a skill grows too large, split it into multiple specialized skills. Shorter, focused skills are more effective because the agent can load exactly what it needs. A broad skill with too much content can overwhelm the agent and reduce the quality of its responses." },
|
||||
{ question: "Where do I create and manage skills?", answer: "Go to Settings and select Skills under the Tools section. From there you can add new skills with a name (kebab-case identifier, max 64 characters), description (max 1024 characters), and content (full instructions in markdown). You can also edit or delete existing skills from this page." },
|
||||
]} />
|
||||
|
||||
@@ -1,629 +0,0 @@
|
||||
---
|
||||
title: AgentPhone
|
||||
description: Provision numbers, send SMS and iMessage, and place voice calls with AgentPhone
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="agentphone"
|
||||
color="linear-gradient(135deg, #1a1a1a 0%, #0a2a14 100%)"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[AgentPhone](https://agentphone.to/) is an API-first voice and messaging platform built for AI agents. AgentPhone lets you provision real phone numbers, place outbound AI voice calls, send SMS and iMessage, manage conversations and contacts, and monitor usage — all through a simple REST API designed for programmatic access.
|
||||
|
||||
**Why AgentPhone?**
|
||||
- **Agent-Native Telephony:** Purpose-built for AI agents — provision numbers, place calls, and send messages without carrier contracts or telephony plumbing.
|
||||
- **Voice + Messaging in One API:** Drive outbound AI voice calls alongside SMS, MMS, and iMessage from the same account and phone numbers.
|
||||
- **Conversation & Transcript Management:** Every call returns an ordered transcript; every message thread is tracked as a conversation with full history and metadata.
|
||||
- **Contacts Built In:** Create, search, update, and delete contacts on the account so your agents can reference people by name instead of raw phone numbers.
|
||||
- **Usage Visibility:** Inspect plan limits, current counts, and daily/monthly aggregation so workflows can stay inside guardrails.
|
||||
|
||||
**Using AgentPhone in Sim**
|
||||
|
||||
Sim's AgentPhone integration connects your agentic workflows directly to AgentPhone using an API key. With 22 operations spanning numbers, calls, conversations, contacts, and usage, you can build powerful voice and messaging automations without writing backend code.
|
||||
|
||||
**Key benefits of using AgentPhone in Sim:**
|
||||
- **Dynamic number provisioning:** Reserve US or Canadian numbers on the fly — per agent, per customer, or per workflow — and release them when no longer needed.
|
||||
- **Outbound AI voice calls:** Place calls from an agent with an optional greeting, voice override, or system prompt, and read the full transcript back as structured data once the call completes.
|
||||
- **Two-way messaging:** Send SMS, MMS, or iMessage, fetch conversation history, and react to incoming iMessages — all from inside your workflow.
|
||||
- **Contact and metadata management:** Keep an account-level contact list and attach custom JSON metadata to conversations so downstream blocks can branch on state.
|
||||
- **Operational insight:** Pull current usage stats and daily/monthly breakdowns to monitor consumption and enforce plan limits before making the next call.
|
||||
|
||||
Whether you're building an outbound AI voice agent, running automated SMS follow-ups, managing two-way customer conversations, or monitoring phone usage across your organization, AgentPhone in Sim gives you direct, secure access to the full AgentPhone API — no middleware required. Simply configure your API key, select the operation you need, and let Sim handle the rest.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Give your workflow a phone. Provision SMS- and voice-enabled numbers, send messages and tapback reactions, place outbound voice calls, manage conversations and contacts, and track usage — all through a single AgentPhone API key.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `agentphone_create_call`
|
||||
|
||||
Initiate an outbound voice call from an AgentPhone agent
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `agentId` | string | Yes | Agent that will handle the call |
|
||||
| `toNumber` | string | Yes | Phone number to call in E.164 format \(e.g. +14155551234\) |
|
||||
| `fromNumberId` | string | No | Phone number ID to use as caller ID. Must belong to the agent. If omitted, the agent's first assigned number is used. |
|
||||
| `initialGreeting` | string | No | Optional greeting spoken when the recipient answers |
|
||||
| `voice` | string | No | Voice ID override for this call \(defaults to the agent's configured voice\) |
|
||||
| `systemPrompt` | string | No | When provided, uses a built-in LLM for the conversation instead of forwarding to your webhook |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Unique call identifier |
|
||||
| `agentId` | string | Agent handling the call |
|
||||
| `status` | string | Initial call status |
|
||||
| `toNumber` | string | Destination phone number |
|
||||
| `fromNumber` | string | Caller ID used for the call |
|
||||
| `phoneNumberId` | string | ID of the phone number used as caller ID |
|
||||
| `direction` | string | Call direction \(outbound\) |
|
||||
| `startedAt` | string | ISO 8601 timestamp |
|
||||
|
||||
### `agentphone_create_contact`
|
||||
|
||||
Create a new contact in AgentPhone
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `phoneNumber` | string | Yes | Phone number in E.164 format \(e.g. +14155551234\) |
|
||||
| `name` | string | Yes | Contact's full name |
|
||||
| `email` | string | No | Contact's email address |
|
||||
| `notes` | string | No | Freeform notes stored on the contact |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Contact ID |
|
||||
| `phoneNumber` | string | Phone number in E.164 format |
|
||||
| `name` | string | Contact name |
|
||||
| `email` | string | Contact email address |
|
||||
| `notes` | string | Freeform notes |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `updatedAt` | string | ISO 8601 update timestamp |
|
||||
|
||||
### `agentphone_create_number`
|
||||
|
||||
Provision a new SMS- and voice-enabled phone number
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `country` | string | No | Two-letter country code \(e.g. US, CA\). Defaults to US. |
|
||||
| `areaCode` | string | No | Preferred area code \(US/CA only, e.g. "415"\). Best-effort — may be ignored if unavailable. |
|
||||
| `agentId` | string | No | Optionally attach the number to an agent immediately |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Unique phone number ID |
|
||||
| `phoneNumber` | string | Provisioned phone number in E.164 format |
|
||||
| `country` | string | Two-letter country code |
|
||||
| `status` | string | Number status \(e.g. active\) |
|
||||
| `type` | string | Number type \(e.g. sms\) |
|
||||
| `agentId` | string | Agent the number is attached to |
|
||||
| `createdAt` | string | ISO 8601 timestamp when the number was created |
|
||||
|
||||
### `agentphone_delete_contact`
|
||||
|
||||
Delete a contact by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `contactId` | string | Yes | Contact ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | ID of the deleted contact |
|
||||
| `deleted` | boolean | Whether the contact was deleted successfully |
|
||||
|
||||
### `agentphone_get_call`
|
||||
|
||||
Fetch a call and its full transcript
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `callId` | string | Yes | ID of the call to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Call ID |
|
||||
| `agentId` | string | Agent that handled the call |
|
||||
| `phoneNumberId` | string | Phone number ID |
|
||||
| `phoneNumber` | string | Phone number used for the call |
|
||||
| `fromNumber` | string | Caller phone number |
|
||||
| `toNumber` | string | Recipient phone number |
|
||||
| `direction` | string | inbound or outbound |
|
||||
| `status` | string | Call status |
|
||||
| `startedAt` | string | ISO 8601 timestamp |
|
||||
| `endedAt` | string | ISO 8601 timestamp |
|
||||
| `durationSeconds` | number | Call duration in seconds |
|
||||
| `lastTranscriptSnippet` | string | Last transcript snippet |
|
||||
| `recordingUrl` | string | Recording audio URL |
|
||||
| `recordingAvailable` | boolean | Whether a recording is available |
|
||||
| `transcripts` | array | Ordered transcript turns for the call |
|
||||
| ↳ `id` | string | Transcript turn ID |
|
||||
| ↳ `transcript` | string | User utterance |
|
||||
| ↳ `confidence` | number | Speech recognition confidence |
|
||||
| ↳ `response` | string | Agent response \(when available\) |
|
||||
| ↳ `createdAt` | string | ISO 8601 timestamp |
|
||||
|
||||
### `agentphone_get_call_transcript`
|
||||
|
||||
Get the full ordered transcript for a call
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `callId` | string | Yes | ID of the call to retrieve the transcript for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `callId` | string | Call ID |
|
||||
| `transcript` | array | Ordered transcript turns for the call |
|
||||
| ↳ `role` | string | Speaker role \(user or agent\) |
|
||||
| ↳ `content` | string | Turn content |
|
||||
| ↳ `createdAt` | string | ISO 8601 timestamp |
|
||||
|
||||
### `agentphone_get_contact`
|
||||
|
||||
Fetch a single contact by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `contactId` | string | Yes | Contact ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Contact ID |
|
||||
| `phoneNumber` | string | Phone number in E.164 format |
|
||||
| `name` | string | Contact name |
|
||||
| `email` | string | Contact email address |
|
||||
| `notes` | string | Freeform notes |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `updatedAt` | string | ISO 8601 update timestamp |
|
||||
|
||||
### `agentphone_get_conversation`
|
||||
|
||||
Get a conversation along with its recent messages
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `conversationId` | string | Yes | Conversation ID |
|
||||
| `messageLimit` | number | No | Number of recent messages to include \(default 50, max 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Conversation ID |
|
||||
| `agentId` | string | Agent ID |
|
||||
| `phoneNumberId` | string | Phone number ID |
|
||||
| `phoneNumber` | string | Phone number |
|
||||
| `participant` | string | External participant phone number |
|
||||
| `lastMessageAt` | string | ISO 8601 timestamp |
|
||||
| `messageCount` | number | Number of messages in the conversation |
|
||||
| `metadata` | json | Custom metadata stored on the conversation |
|
||||
| `createdAt` | string | ISO 8601 timestamp |
|
||||
| `messages` | array | Recent messages in the conversation |
|
||||
| ↳ `id` | string | Message ID |
|
||||
| ↳ `body` | string | Message text |
|
||||
| ↳ `fromNumber` | string | Sender phone number |
|
||||
| ↳ `toNumber` | string | Recipient phone number |
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | sms, mms, or imessage |
|
||||
| ↳ `mediaUrl` | string | Attached media URL |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
|
||||
### `agentphone_get_conversation_messages`
|
||||
|
||||
Get paginated messages for a conversation
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `conversationId` | string | Yes | Conversation ID |
|
||||
| `limit` | number | No | Number of messages to return \(default 50, max 200\) |
|
||||
| `before` | string | No | Return messages received before this ISO 8601 timestamp |
|
||||
| `after` | string | No | Return messages received after this ISO 8601 timestamp |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Messages in the conversation |
|
||||
| ↳ `id` | string | Message ID |
|
||||
| ↳ `body` | string | Message text |
|
||||
| ↳ `fromNumber` | string | Sender phone number |
|
||||
| ↳ `toNumber` | string | Recipient phone number |
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | sms, mms, or imessage |
|
||||
| ↳ `mediaUrl` | string | Attached media URL |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
| `hasMore` | boolean | Whether more messages are available |
|
||||
|
||||
### `agentphone_get_number_messages`
|
||||
|
||||
Fetch messages received on a specific phone number
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `numberId` | string | Yes | ID of the phone number |
|
||||
| `limit` | number | No | Number of messages to return \(default 50, max 200\) |
|
||||
| `before` | string | No | Return messages received before this ISO 8601 timestamp |
|
||||
| `after` | string | No | Return messages received after this ISO 8601 timestamp |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Messages received on the number |
|
||||
| ↳ `id` | string | Message ID |
|
||||
| ↳ `from_` | string | Sender phone number \(E.164\) |
|
||||
| ↳ `to` | string | Recipient phone number \(E.164\) |
|
||||
| ↳ `body` | string | Message text |
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | Channel \(sms, mms, etc.\) |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
| `hasMore` | boolean | Whether more messages are available |
|
||||
|
||||
### `agentphone_get_usage`
|
||||
|
||||
Retrieve current usage statistics for the AgentPhone account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `plan` | json | Plan name and limits \(name, limits: numbers/messagesPerMonth/voiceMinutesPerMonth/maxCallDurationMinutes/concurrentCalls\) |
|
||||
| `numbers` | json | Phone number usage \(used, limit, remaining\) |
|
||||
| `stats` | json | Usage stats: totalMessages, messagesLast24h/7d/30d, totalCalls, callsLast24h/7d/30d, totalWebhookDeliveries, successfulWebhookDeliveries, failedWebhookDeliveries |
|
||||
| `periodStart` | string | Billing period start |
|
||||
| `periodEnd` | string | Billing period end |
|
||||
|
||||
### `agentphone_get_usage_daily`
|
||||
|
||||
Get a daily breakdown of usage (messages, calls, webhooks) for the last N days
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `days` | number | No | Number of days to return \(1-365, default 30\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Daily usage entries |
|
||||
| ↳ `date` | string | Day \(YYYY-MM-DD\) |
|
||||
| ↳ `messages` | number | Messages that day |
|
||||
| ↳ `calls` | number | Calls that day |
|
||||
| ↳ `webhooks` | number | Webhook deliveries that day |
|
||||
| `days` | number | Number of days returned |
|
||||
|
||||
### `agentphone_get_usage_monthly`
|
||||
|
||||
Get monthly usage aggregation (messages, calls, webhooks) for the last N months
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `months` | number | No | Number of months to return \(1-24, default 6\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Monthly usage entries |
|
||||
| ↳ `month` | string | Month \(YYYY-MM\) |
|
||||
| ↳ `messages` | number | Messages that month |
|
||||
| ↳ `calls` | number | Calls that month |
|
||||
| ↳ `webhooks` | number | Webhook deliveries that month |
|
||||
| `months` | number | Number of months returned |
|
||||
|
||||
### `agentphone_list_calls`
|
||||
|
||||
List voice calls for this AgentPhone account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `limit` | number | No | Number of results to return \(default 20, max 100\) |
|
||||
| `offset` | number | No | Number of results to skip \(min 0\) |
|
||||
| `status` | string | No | Filter by status \(completed, in-progress, failed\) |
|
||||
| `direction` | string | No | Filter by direction \(inbound, outbound\) |
|
||||
| `type` | string | No | Filter by call type \(pstn, web\) |
|
||||
| `search` | string | No | Search by phone number \(matches fromNumber or toNumber\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Calls |
|
||||
| ↳ `id` | string | Call ID |
|
||||
| ↳ `agentId` | string | Agent that handled the call |
|
||||
| ↳ `phoneNumberId` | string | Phone number ID used for the call |
|
||||
| ↳ `phoneNumber` | string | Phone number used for the call |
|
||||
| ↳ `fromNumber` | string | Caller phone number |
|
||||
| ↳ `toNumber` | string | Recipient phone number |
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `status` | string | Call status |
|
||||
| ↳ `startedAt` | string | ISO 8601 timestamp |
|
||||
| ↳ `endedAt` | string | ISO 8601 timestamp |
|
||||
| ↳ `durationSeconds` | number | Call duration in seconds |
|
||||
| ↳ `lastTranscriptSnippet` | string | Last transcript snippet |
|
||||
| ↳ `recordingUrl` | string | Recording audio URL |
|
||||
| ↳ `recordingAvailable` | boolean | Whether a recording is available |
|
||||
| `hasMore` | boolean | Whether more results are available |
|
||||
| `total` | number | Total number of matching calls |
|
||||
|
||||
### `agentphone_list_contacts`
|
||||
|
||||
List contacts for this AgentPhone account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `search` | string | No | Filter by name or phone number \(case-insensitive contains\) |
|
||||
| `limit` | number | No | Number of results to return \(default 50\) |
|
||||
| `offset` | number | No | Number of results to skip \(min 0\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Contacts |
|
||||
| ↳ `id` | string | Contact ID |
|
||||
| ↳ `phoneNumber` | string | Phone number in E.164 format |
|
||||
| ↳ `name` | string | Contact name |
|
||||
| ↳ `email` | string | Contact email address |
|
||||
| ↳ `notes` | string | Freeform notes |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 update timestamp |
|
||||
| `hasMore` | boolean | Whether more results are available |
|
||||
| `total` | number | Total number of contacts |
|
||||
|
||||
### `agentphone_list_conversations`
|
||||
|
||||
List conversations (message threads) for this AgentPhone account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `limit` | number | No | Number of results to return \(default 20, max 100\) |
|
||||
| `offset` | number | No | Number of results to skip \(min 0\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Conversations |
|
||||
| ↳ `id` | string | Conversation ID |
|
||||
| ↳ `agentId` | string | Agent ID |
|
||||
| ↳ `phoneNumberId` | string | Phone number ID |
|
||||
| ↳ `phoneNumber` | string | Phone number |
|
||||
| ↳ `participant` | string | External participant phone number |
|
||||
| ↳ `lastMessageAt` | string | ISO 8601 timestamp |
|
||||
| ↳ `lastMessagePreview` | string | Last message preview |
|
||||
| ↳ `messageCount` | number | Number of messages in the conversation |
|
||||
| ↳ `metadata` | json | Custom metadata stored on the conversation |
|
||||
| ↳ `createdAt` | string | ISO 8601 timestamp |
|
||||
| `hasMore` | boolean | Whether more results are available |
|
||||
| `total` | number | Total number of conversations |
|
||||
|
||||
### `agentphone_list_numbers`
|
||||
|
||||
List all phone numbers provisioned for this AgentPhone account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `limit` | number | No | Number of results to return \(default 20, max 100\) |
|
||||
| `offset` | number | No | Number of results to skip \(min 0\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Phone numbers |
|
||||
| ↳ `id` | string | Phone number ID |
|
||||
| ↳ `phoneNumber` | string | Phone number in E.164 format |
|
||||
| ↳ `country` | string | Two-letter country code |
|
||||
| ↳ `status` | string | Number status |
|
||||
| ↳ `type` | string | Number type \(e.g. sms\) |
|
||||
| ↳ `agentId` | string | Attached agent ID |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `hasMore` | boolean | Whether more results are available |
|
||||
| `total` | number | Total number of phone numbers |
|
||||
|
||||
### `agentphone_react_to_message`
|
||||
|
||||
Send an iMessage tapback reaction to a message (iMessage only)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `messageId` | string | Yes | ID of the message to react to |
|
||||
| `reaction` | string | Yes | Reaction type: love, like, dislike, laugh, emphasize, or question |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Reaction ID |
|
||||
| `reactionType` | string | Reaction type applied |
|
||||
| `messageId` | string | ID of the message that was reacted to |
|
||||
| `channel` | string | Channel \(imessage\) |
|
||||
|
||||
### `agentphone_release_number`
|
||||
|
||||
Release (delete) a phone number. This action is irreversible.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `numberId` | string | Yes | ID of the phone number to release |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | ID of the released phone number |
|
||||
| `released` | boolean | Whether the number was released successfully |
|
||||
|
||||
### `agentphone_send_message`
|
||||
|
||||
Send an outbound SMS or iMessage from an AgentPhone agent
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `agentId` | string | Yes | Agent sending the message |
|
||||
| `toNumber` | string | Yes | Recipient phone number in E.164 format \(e.g. +14155551234\) |
|
||||
| `body` | string | Yes | Message text to send |
|
||||
| `mediaUrl` | string | No | Optional URL of an image, video, or file to attach |
|
||||
| `numberId` | string | No | Phone number ID to send from. If omitted, the agent's first assigned number is used. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Message ID |
|
||||
| `status` | string | Delivery status |
|
||||
| `channel` | string | sms, mms, or imessage |
|
||||
| `fromNumber` | string | Sender phone number |
|
||||
| `toNumber` | string | Recipient phone number |
|
||||
|
||||
### `agentphone_update_contact`
|
||||
|
||||
Update a contact
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `contactId` | string | Yes | Contact ID |
|
||||
| `phoneNumber` | string | No | New phone number in E.164 format |
|
||||
| `name` | string | No | New contact name |
|
||||
| `email` | string | No | New email address |
|
||||
| `notes` | string | No | New freeform notes |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Contact ID |
|
||||
| `phoneNumber` | string | Phone number in E.164 format |
|
||||
| `name` | string | Contact name |
|
||||
| `email` | string | Contact email address |
|
||||
| `notes` | string | Freeform notes |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `updatedAt` | string | ISO 8601 update timestamp |
|
||||
|
||||
### `agentphone_update_conversation`
|
||||
|
||||
Update conversation metadata (stored state). Pass null to clear existing metadata.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `conversationId` | string | Yes | Conversation ID |
|
||||
| `metadata` | json | No | Custom key-value metadata to store on the conversation. Pass null to clear existing metadata. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Conversation ID |
|
||||
| `agentId` | string | Agent ID |
|
||||
| `phoneNumberId` | string | Phone number ID |
|
||||
| `phoneNumber` | string | Phone number |
|
||||
| `participant` | string | External participant phone number |
|
||||
| `lastMessageAt` | string | ISO 8601 timestamp |
|
||||
| `messageCount` | number | Number of messages |
|
||||
| `metadata` | json | Custom metadata stored on the conversation |
|
||||
| `createdAt` | string | ISO 8601 timestamp |
|
||||
| `messages` | array | Messages in the conversation |
|
||||
| ↳ `id` | string | Message ID |
|
||||
| ↳ `body` | string | Message body |
|
||||
| ↳ `fromNumber` | string | Sender phone number |
|
||||
| ↳ `toNumber` | string | Recipient phone number |
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | Channel \(sms, mms, etc.\) |
|
||||
| ↳ `mediaUrl` | string | Media URL if any |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ Integrate Ashby into the workflow. Manage candidates (list, get, create, update,
|
||||
|
||||
### `ashby_add_candidate_tag`
|
||||
|
||||
Adds a tag to a candidate in Ashby and returns the updated candidate.
|
||||
Adds a tag to a candidate in Ashby.
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -52,37 +52,7 @@ Adds a tag to a candidate in Ashby and returns the updated candidate.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `success` | boolean | Whether the tag was successfully added |
|
||||
|
||||
### `ashby_change_application_stage`
|
||||
|
||||
@@ -101,37 +71,8 @@ Moves an application to a different interview stage. Requires an archive reason
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `applicationId` | string | Application UUID |
|
||||
| `stageId` | string | New interview stage UUID |
|
||||
|
||||
### `ashby_create_application`
|
||||
|
||||
@@ -154,37 +95,7 @@ Creates a new application for a candidate on a job. Optionally specify interview
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `applicationId` | string | Created application UUID |
|
||||
|
||||
### `ashby_create_candidate`
|
||||
|
||||
@@ -196,7 +107,7 @@ Creates a new candidate record in Ashby.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Ashby API Key |
|
||||
| `name` | string | Yes | The candidate full name |
|
||||
| `email` | string | No | Primary email address for the candidate |
|
||||
| `email` | string | Yes | Primary email address for the candidate |
|
||||
| `phoneNumber` | string | No | Primary phone number for the candidate |
|
||||
| `linkedInUrl` | string | No | LinkedIn profile URL |
|
||||
| `githubUrl` | string | No | GitHub profile URL |
|
||||
@@ -206,37 +117,17 @@ Creates a new candidate record in Ashby.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `id` | string | Created candidate UUID |
|
||||
| `name` | string | Full name |
|
||||
| `primaryEmailAddress` | object | Primary email contact info |
|
||||
| ↳ `value` | string | Email address |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary email |
|
||||
| `primaryPhoneNumber` | object | Primary phone contact info |
|
||||
| ↳ `value` | string | Phone number |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary phone |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
|
||||
### `ashby_create_note`
|
||||
|
||||
@@ -256,15 +147,7 @@ Creates a note on a candidate in Ashby. Supports plain text and HTML content (bo
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Created note UUID |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `content` | string | Note content |
|
||||
| `author` | object | Author of the note |
|
||||
| ↳ `id` | string | Author user UUID |
|
||||
| ↳ `firstName` | string | Author first name |
|
||||
| ↳ `lastName` | string | Author last name |
|
||||
| ↳ `email` | string | Author email |
|
||||
| `noteId` | string | Created note UUID |
|
||||
|
||||
### `ashby_get_application`
|
||||
|
||||
@@ -281,37 +164,28 @@ Retrieves full details about a single application by its ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `id` | string | Application UUID |
|
||||
| `status` | string | Application status \(Active, Hired, Archived, Lead\) |
|
||||
| `candidate` | object | Associated candidate |
|
||||
| ↳ `id` | string | Candidate UUID |
|
||||
| ↳ `name` | string | Candidate name |
|
||||
| `job` | object | Associated job |
|
||||
| ↳ `id` | string | Job UUID |
|
||||
| ↳ `title` | string | Job title |
|
||||
| `currentInterviewStage` | object | Current interview stage |
|
||||
| ↳ `id` | string | Stage UUID |
|
||||
| ↳ `title` | string | Stage title |
|
||||
| ↳ `type` | string | Stage type |
|
||||
| `source` | object | Application source |
|
||||
| ↳ `id` | string | Source UUID |
|
||||
| ↳ `title` | string | Source title |
|
||||
| `archiveReason` | object | Reason for archival |
|
||||
| ↳ `id` | string | Reason UUID |
|
||||
| ↳ `text` | string | Reason text |
|
||||
| ↳ `reasonType` | string | Reason type |
|
||||
| `archivedAt` | string | ISO 8601 archive timestamp |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_get_candidate`
|
||||
|
||||
@@ -328,37 +202,27 @@ Retrieves full details about a single candidate by their ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `id` | string | Candidate UUID |
|
||||
| `name` | string | Full name |
|
||||
| `primaryEmailAddress` | object | Primary email contact info |
|
||||
| ↳ `value` | string | Email address |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary email |
|
||||
| `primaryPhoneNumber` | object | Primary phone contact info |
|
||||
| ↳ `value` | string | Phone number |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary phone |
|
||||
| `profileUrl` | string | URL to the candidate Ashby profile |
|
||||
| `position` | string | Current position or title |
|
||||
| `company` | string | Current company |
|
||||
| `linkedInUrl` | string | LinkedIn profile URL |
|
||||
| `githubUrl` | string | GitHub profile URL |
|
||||
| `tags` | array | Tags applied to the candidate |
|
||||
| ↳ `id` | string | Tag UUID |
|
||||
| ↳ `title` | string | Tag title |
|
||||
| `applicationIds` | array | IDs of associated applications |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_get_job`
|
||||
|
||||
@@ -375,37 +239,16 @@ Retrieves full details about a single job by its ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `id` | string | Job UUID |
|
||||
| `title` | string | Job title |
|
||||
| `status` | string | Job status \(Open, Closed, Draft, Archived\) |
|
||||
| `employmentType` | string | Employment type \(FullTime, PartTime, Intern, Contract, Temporary\) |
|
||||
| `departmentId` | string | Department UUID |
|
||||
| `locationId` | string | Location UUID |
|
||||
| `descriptionPlain` | string | Job description in plain text |
|
||||
| `isArchived` | boolean | Whether the job is archived |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_get_job_posting`
|
||||
|
||||
@@ -417,8 +260,6 @@ Retrieves full details about a single job posting by its ID.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Ashby API Key |
|
||||
| `jobPostingId` | string | Yes | The UUID of the job posting to fetch |
|
||||
| `expandApplicationFormDefinition` | boolean | No | Include application form definition in the response |
|
||||
| `expandSurveyFormDefinitions` | boolean | No | Include survey form definitions in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -426,56 +267,14 @@ Retrieves full details about a single job posting by its ID.
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Job posting UUID |
|
||||
| `title` | string | Job posting title |
|
||||
| `descriptionPlain` | string | Full description in plain text |
|
||||
| `descriptionHtml` | string | Full description in HTML |
|
||||
| `descriptionSocial` | string | Shortened description for social sharing \(max 200 chars\) |
|
||||
| `descriptionParts` | object | Description broken into opening, body, and closing sections |
|
||||
| ↳ `descriptionOpening` | object | Opening \(from Job Boards theme settings\) |
|
||||
| ↳ `html` | string | HTML content |
|
||||
| ↳ `plain` | string | Plain text content |
|
||||
| ↳ `descriptionBody` | object | Main description body |
|
||||
| ↳ `html` | string | HTML content |
|
||||
| ↳ `plain` | string | Plain text content |
|
||||
| ↳ `descriptionClosing` | object | Closing \(from Job Boards theme settings\) |
|
||||
| ↳ `html` | string | HTML content |
|
||||
| ↳ `plain` | string | Plain text content |
|
||||
| `departmentName` | string | Department name |
|
||||
| `teamName` | string | Team name |
|
||||
| `teamNameHierarchy` | array | Hierarchy of team names from root to team |
|
||||
| `jobId` | string | Associated job UUID |
|
||||
| `locationName` | string | Primary location name |
|
||||
| `locationIds` | object | Primary and secondary location UUIDs |
|
||||
| ↳ `primaryLocationId` | string | Primary location UUID |
|
||||
| ↳ `secondaryLocationIds` | array | Secondary location UUIDs |
|
||||
| `address` | object | Postal address of the posting location |
|
||||
| ↳ `postalAddress` | object | Structured postal address |
|
||||
| ↳ `addressCountry` | string | Country |
|
||||
| ↳ `addressRegion` | string | State or region |
|
||||
| ↳ `addressLocality` | string | City or locality |
|
||||
| ↳ `postalCode` | string | Postal code |
|
||||
| ↳ `streetAddress` | string | Street address |
|
||||
| `isRemote` | boolean | Whether the posting is remote |
|
||||
| `workplaceType` | string | Workplace type \(OnSite, Remote, Hybrid\) |
|
||||
| `employmentType` | string | Employment type \(FullTime, PartTime, Intern, Contract, Temporary\) |
|
||||
| `isListed` | boolean | Whether publicly listed on the job board |
|
||||
| `suppressDescriptionOpening` | boolean | Whether the theme opening is hidden on this posting |
|
||||
| `suppressDescriptionClosing` | boolean | Whether the theme closing is hidden on this posting |
|
||||
| `locationName` | string | Location name |
|
||||
| `departmentName` | string | Department name |
|
||||
| `employmentType` | string | Employment type \(e.g. FullTime, PartTime, Contract\) |
|
||||
| `descriptionPlain` | string | Job posting description in plain text |
|
||||
| `isListed` | boolean | Whether the posting is publicly listed |
|
||||
| `publishedDate` | string | ISO 8601 published date |
|
||||
| `applicationDeadline` | string | ISO 8601 application deadline |
|
||||
| `externalLink` | string | External link to the job posting |
|
||||
| `applyLink` | string | Direct apply link |
|
||||
| `compensation` | object | Compensation details for the posting |
|
||||
| ↳ `compensationTierSummary` | string | Human-readable tier summary |
|
||||
| ↳ `summaryComponents` | array | Structured compensation components |
|
||||
| ↳ `summary` | string | Component summary |
|
||||
| ↳ `compensationTypeLabel` | string | Component type label \(Salary, Commission, Bonus, Equity, etc.\) |
|
||||
| ↳ `interval` | string | Payment interval \(e.g. annual, hourly\) |
|
||||
| ↳ `currencyCode` | string | ISO 4217 currency code |
|
||||
| ↳ `minValue` | number | Minimum value |
|
||||
| ↳ `maxValue` | number | Maximum value |
|
||||
| ↳ `shouldDisplayCompensationOnJobBoard` | boolean | Whether compensation is shown on the job board |
|
||||
| `applicationLimitCalloutHtml` | string | HTML callout shown when application limit is reached |
|
||||
| `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_get_offer`
|
||||
|
||||
@@ -492,41 +291,20 @@ Retrieves full details about a single offer by its ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `id` | string | Offer UUID |
|
||||
| `offerStatus` | string | Offer status \(e.g. WaitingOnCandidateResponse, CandidateAccepted\) |
|
||||
| `acceptanceStatus` | string | Acceptance status \(e.g. Accepted, Declined, Pending\) |
|
||||
| `applicationId` | string | Associated application UUID |
|
||||
| `startDate` | string | Offer start date |
|
||||
| `salary` | object | Salary details |
|
||||
| ↳ `currencyCode` | string | ISO 4217 currency code |
|
||||
| ↳ `value` | number | Salary amount |
|
||||
| `openingId` | string | Associated opening UUID |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp \(from latest version\) |
|
||||
|
||||
### `ashby_list_applications`
|
||||
|
||||
Lists all applications in an Ashby organization with pagination and optional filters for status, job, and creation date.
|
||||
Lists all applications in an Ashby organization with pagination and optional filters for status, job, candidate, and creation date.
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -537,6 +315,7 @@ Lists all applications in an Ashby organization with pagination and optional fil
|
||||
| `perPage` | number | No | Number of results per page \(default 100\) |
|
||||
| `status` | string | No | Filter by application status: Active, Hired, Archived, or Lead |
|
||||
| `jobId` | string | No | Filter applications by a specific job UUID |
|
||||
| `candidateId` | string | No | Filter applications by a specific candidate UUID |
|
||||
| `createdAfter` | string | No | Filter to applications created after this ISO 8601 timestamp \(e.g. 2024-01-01T00:00:00Z\) |
|
||||
|
||||
#### Output
|
||||
@@ -544,6 +323,23 @@ Lists all applications in an Ashby organization with pagination and optional fil
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `applications` | array | List of applications |
|
||||
| ↳ `id` | string | Application UUID |
|
||||
| ↳ `status` | string | Application status \(Active, Hired, Archived, Lead\) |
|
||||
| ↳ `candidate` | object | Associated candidate |
|
||||
| ↳ `id` | string | Candidate UUID |
|
||||
| ↳ `name` | string | Candidate name |
|
||||
| ↳ `job` | object | Associated job |
|
||||
| ↳ `id` | string | Job UUID |
|
||||
| ↳ `title` | string | Job title |
|
||||
| ↳ `currentInterviewStage` | object | Current interview stage |
|
||||
| ↳ `id` | string | Stage UUID |
|
||||
| ↳ `title` | string | Stage title |
|
||||
| ↳ `type` | string | Stage type |
|
||||
| ↳ `source` | object | Application source |
|
||||
| ↳ `id` | string | Source UUID |
|
||||
| ↳ `title` | string | Source title |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -556,7 +352,6 @@ Lists all archive reasons configured in Ashby.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Ashby API Key |
|
||||
| `includeArchived` | boolean | No | Whether to include archived archive reasons in the response \(default false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -565,7 +360,7 @@ Lists all archive reasons configured in Ashby.
|
||||
| `archiveReasons` | array | List of archive reasons |
|
||||
| ↳ `id` | string | Archive reason UUID |
|
||||
| ↳ `text` | string | Archive reason text |
|
||||
| ↳ `reasonType` | string | Reason type \(RejectedByCandidate, RejectedByOrg, Other\) |
|
||||
| ↳ `reasonType` | string | Reason type |
|
||||
| ↳ `isArchived` | boolean | Whether the reason is archived |
|
||||
|
||||
### `ashby_list_candidate_tags`
|
||||
@@ -577,10 +372,6 @@ Lists all candidate tags configured in Ashby.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Ashby API Key |
|
||||
| `includeArchived` | boolean | No | Whether to include archived candidate tags \(default false\) |
|
||||
| `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value |
|
||||
| `syncToken` | string | No | Sync token from a previous response to fetch only changed results |
|
||||
| `perPage` | number | No | Number of results per page \(default 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -590,9 +381,6 @@ Lists all candidate tags configured in Ashby.
|
||||
| ↳ `id` | string | Tag UUID |
|
||||
| ↳ `title` | string | Tag title |
|
||||
| ↳ `isArchived` | boolean | Whether the tag is archived |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
| `syncToken` | string | Sync token to use for incremental updates in future requests |
|
||||
|
||||
### `ashby_list_candidates`
|
||||
|
||||
@@ -611,6 +399,18 @@ Lists all candidates in an Ashby organization with cursor-based pagination.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | array | List of candidates |
|
||||
| ↳ `id` | string | Candidate UUID |
|
||||
| ↳ `name` | string | Full name |
|
||||
| ↳ `primaryEmailAddress` | object | Primary email contact info |
|
||||
| ↳ `value` | string | Email address |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary email |
|
||||
| ↳ `primaryPhoneNumber` | object | Primary phone contact info |
|
||||
| ↳ `value` | string | Phone number |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary phone |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -631,15 +431,9 @@ Lists all custom field definitions configured in Ashby.
|
||||
| `customFields` | array | List of custom field definitions |
|
||||
| ↳ `id` | string | Custom field UUID |
|
||||
| ↳ `title` | string | Custom field title |
|
||||
| ↳ `isPrivate` | boolean | Whether the custom field is private |
|
||||
| ↳ `fieldType` | string | Field data type \(MultiValueSelect, NumberRange, String, Date, ValueSelect, Number, Currency, Boolean, LongText, CompensationRange\) |
|
||||
| ↳ `objectType` | string | Object type the field applies to \(Application, Candidate, Employee, Job, Offer, Opening, Talent_Project\) |
|
||||
| ↳ `fieldType` | string | Field type \(e.g. String, Number, Boolean\) |
|
||||
| ↳ `objectType` | string | Object type the field applies to \(e.g. Candidate, Application, Job\) |
|
||||
| ↳ `isArchived` | boolean | Whether the custom field is archived |
|
||||
| ↳ `isRequired` | boolean | Whether a value is required |
|
||||
| ↳ `selectableValues` | array | Selectable values for MultiValueSelect fields \(empty for other field types\) |
|
||||
| ↳ `label` | string | Display label |
|
||||
| ↳ `value` | string | Stored value |
|
||||
| ↳ `isArchived` | boolean | Whether archived |
|
||||
|
||||
### `ashby_list_departments`
|
||||
|
||||
@@ -658,11 +452,8 @@ Lists all departments in Ashby.
|
||||
| `departments` | array | List of departments |
|
||||
| ↳ `id` | string | Department UUID |
|
||||
| ↳ `name` | string | Department name |
|
||||
| ↳ `externalName` | string | Candidate-facing name used on job boards |
|
||||
| ↳ `isArchived` | boolean | Whether the department is archived |
|
||||
| ↳ `parentId` | string | Parent department UUID |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_list_interviews`
|
||||
|
||||
@@ -684,24 +475,10 @@ Lists interview schedules in Ashby, optionally filtered by application or interv
|
||||
| --------- | ---- | ----------- |
|
||||
| `interviewSchedules` | array | List of interview schedules |
|
||||
| ↳ `id` | string | Interview schedule UUID |
|
||||
| ↳ `status` | string | Schedule status \(NeedsScheduling, WaitingOnCandidateBooking, Scheduled, Complete, Cancelled, OnHold, etc.\) |
|
||||
| ↳ `applicationId` | string | Associated application UUID |
|
||||
| ↳ `interviewStageId` | string | Interview stage UUID |
|
||||
| ↳ `status` | string | Schedule status |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
| ↳ `interviewEvents` | array | Scheduled interview events on this schedule |
|
||||
| ↳ `id` | string | Event UUID |
|
||||
| ↳ `interviewId` | string | Interview template UUID |
|
||||
| ↳ `interviewScheduleId` | string | Parent schedule UUID |
|
||||
| ↳ `interviewerUserIds` | array | User UUIDs of interviewers assigned to the event |
|
||||
| ↳ `createdAt` | string | Event creation timestamp |
|
||||
| ↳ `updatedAt` | string | Event last updated timestamp |
|
||||
| ↳ `startTime` | string | Event start time |
|
||||
| ↳ `endTime` | string | Event end time |
|
||||
| ↳ `feedbackLink` | string | URL to submit feedback for the event |
|
||||
| ↳ `location` | string | Physical location |
|
||||
| ↳ `meetingLink` | string | Virtual meeting URL |
|
||||
| ↳ `hasSubmittedFeedback` | boolean | Whether any feedback has been submitted |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -723,22 +500,11 @@ Lists all job postings in Ashby.
|
||||
| ↳ `id` | string | Job posting UUID |
|
||||
| ↳ `title` | string | Job posting title |
|
||||
| ↳ `jobId` | string | Associated job UUID |
|
||||
| ↳ `locationName` | string | Location name |
|
||||
| ↳ `departmentName` | string | Department name |
|
||||
| ↳ `teamName` | string | Team name |
|
||||
| ↳ `locationName` | string | Primary location display name |
|
||||
| ↳ `locationIds` | object | Primary and secondary location UUIDs |
|
||||
| ↳ `primaryLocationId` | string | Primary location UUID |
|
||||
| ↳ `secondaryLocationIds` | array | Secondary location UUIDs |
|
||||
| ↳ `workplaceType` | string | Workplace type \(OnSite, Remote, Hybrid\) |
|
||||
| ↳ `employmentType` | string | Employment type \(FullTime, PartTime, Intern, Contract, Temporary\) |
|
||||
| ↳ `employmentType` | string | Employment type \(e.g. FullTime, PartTime, Contract\) |
|
||||
| ↳ `isListed` | boolean | Whether the posting is publicly listed |
|
||||
| ↳ `publishedDate` | string | ISO 8601 published date |
|
||||
| ↳ `applicationDeadline` | string | ISO 8601 application deadline |
|
||||
| ↳ `externalLink` | string | External link to the job posting |
|
||||
| ↳ `applyLink` | string | Direct apply link for the job posting |
|
||||
| ↳ `compensationTierSummary` | string | Compensation tier summary for job boards |
|
||||
| ↳ `shouldDisplayCompensationOnJobBoard` | boolean | Whether compensation is shown on the job board |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_list_jobs`
|
||||
|
||||
@@ -758,6 +524,14 @@ Lists all jobs in an Ashby organization. By default returns Open, Closed, and Ar
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `jobs` | array | List of jobs |
|
||||
| ↳ `id` | string | Job UUID |
|
||||
| ↳ `title` | string | Job title |
|
||||
| ↳ `status` | string | Job status \(Open, Closed, Archived, Draft\) |
|
||||
| ↳ `employmentType` | string | Employment type \(FullTime, PartTime, Intern, Contract, Temporary\) |
|
||||
| ↳ `departmentId` | string | Department UUID |
|
||||
| ↳ `locationId` | string | Location UUID |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -778,18 +552,12 @@ Lists all locations configured in Ashby.
|
||||
| `locations` | array | List of locations |
|
||||
| ↳ `id` | string | Location UUID |
|
||||
| ↳ `name` | string | Location name |
|
||||
| ↳ `externalName` | string | Candidate-facing name used on job boards |
|
||||
| ↳ `isArchived` | boolean | Whether the location is archived |
|
||||
| ↳ `isRemote` | boolean | Whether the location is remote \(use workplaceType instead\) |
|
||||
| ↳ `workplaceType` | string | Workplace type \(OnSite, Hybrid, Remote\) |
|
||||
| ↳ `parentLocationId` | string | Parent location UUID |
|
||||
| ↳ `type` | string | Location component type \(Location, LocationHierarchy\) |
|
||||
| ↳ `address` | object | Location postal address |
|
||||
| ↳ `addressCountry` | string | Country |
|
||||
| ↳ `addressRegion` | string | State or region |
|
||||
| ↳ `addressLocality` | string | City or locality |
|
||||
| ↳ `postalCode` | string | Postal code |
|
||||
| ↳ `streetAddress` | string | Street address |
|
||||
| ↳ `isRemote` | boolean | Whether this is a remote location |
|
||||
| ↳ `address` | object | Location address |
|
||||
| ↳ `city` | string | City |
|
||||
| ↳ `region` | string | State or region |
|
||||
| ↳ `country` | string | Country |
|
||||
|
||||
### `ashby_list_notes`
|
||||
|
||||
@@ -811,7 +579,6 @@ Lists all notes on a candidate with pagination support.
|
||||
| `notes` | array | List of notes on the candidate |
|
||||
| ↳ `id` | string | Note UUID |
|
||||
| ↳ `content` | string | Note content |
|
||||
| ↳ `isPrivate` | boolean | Whether the note is private |
|
||||
| ↳ `author` | object | Note author |
|
||||
| ↳ `id` | string | Author user UUID |
|
||||
| ↳ `firstName` | string | First name |
|
||||
@@ -838,6 +605,16 @@ Lists all offers with their latest version in an Ashby organization.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `offers` | array | List of offers |
|
||||
| ↳ `id` | string | Offer UUID |
|
||||
| ↳ `offerStatus` | string | Offer status |
|
||||
| ↳ `acceptanceStatus` | string | Acceptance status |
|
||||
| ↳ `applicationId` | string | Associated application UUID |
|
||||
| ↳ `startDate` | string | Offer start date |
|
||||
| ↳ `salary` | object | Salary details |
|
||||
| ↳ `currencyCode` | string | ISO 4217 currency code |
|
||||
| ↳ `value` | number | Salary amount |
|
||||
| ↳ `openingId` | string | Associated opening UUID |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -857,6 +634,12 @@ Lists all openings in Ashby with pagination.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `openings` | array | List of openings |
|
||||
| ↳ `id` | string | Opening UUID |
|
||||
| ↳ `openingState` | string | Opening state \(Approved, Closed, Draft, Filled, Open\) |
|
||||
| ↳ `isArchived` | boolean | Whether the opening is archived |
|
||||
| ↳ `openedAt` | string | ISO 8601 opened timestamp |
|
||||
| ↳ `closedAt` | string | ISO 8601 closed timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
@@ -878,10 +661,6 @@ Lists all candidate sources configured in Ashby.
|
||||
| ↳ `id` | string | Source UUID |
|
||||
| ↳ `title` | string | Source title |
|
||||
| ↳ `isArchived` | boolean | Whether the source is archived |
|
||||
| ↳ `sourceType` | object | Source type grouping |
|
||||
| ↳ `id` | string | Source type UUID |
|
||||
| ↳ `title` | string | Source type title |
|
||||
| ↳ `isArchived` | boolean | Whether archived |
|
||||
|
||||
### `ashby_list_users`
|
||||
|
||||
@@ -900,12 +679,18 @@ Lists all users in Ashby with pagination.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `users` | array | List of users |
|
||||
| ↳ `id` | string | User UUID |
|
||||
| ↳ `firstName` | string | First name |
|
||||
| ↳ `lastName` | string | Last name |
|
||||
| ↳ `email` | string | Email address |
|
||||
| ↳ `isEnabled` | boolean | Whether the user account is enabled |
|
||||
| ↳ `globalRole` | string | User role \(Organization Admin, Elevated Access, Limited Access, External Recruiter\) |
|
||||
| `moreDataAvailable` | boolean | Whether more pages of results exist |
|
||||
| `nextCursor` | string | Opaque cursor for fetching the next page |
|
||||
|
||||
### `ashby_remove_candidate_tag`
|
||||
|
||||
Removes a tag from a candidate in Ashby and returns the updated candidate.
|
||||
Removes a tag from a candidate in Ashby.
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -919,37 +704,7 @@ Removes a tag from a candidate in Ashby and returns the updated candidate.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `success` | boolean | Whether the tag was successfully removed |
|
||||
|
||||
### `ashby_search_candidates`
|
||||
|
||||
@@ -968,6 +723,18 @@ Searches for candidates by name and/or email with AND logic. Results are limited
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | array | Matching candidates \(max 100 results\) |
|
||||
| ↳ `id` | string | Candidate UUID |
|
||||
| ↳ `name` | string | Full name |
|
||||
| ↳ `primaryEmailAddress` | object | Primary email contact info |
|
||||
| ↳ `value` | string | Email address |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary email |
|
||||
| ↳ `primaryPhoneNumber` | object | Primary phone contact info |
|
||||
| ↳ `value` | string | Phone number |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary phone |
|
||||
| ↳ `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
### `ashby_update_candidate`
|
||||
|
||||
@@ -991,36 +758,26 @@ Updates an existing candidate record in Ashby. Only provided fields are changed.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `candidates` | json | List of candidates with rich fields \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], linkedInUrl, githubUrl, profileUrl, position, company, school, timezone, location with locationComponents\[\], tags\[\], applicationIds\[\], customFields\[\], resumeFileHandle, fileHandles\[\], source with sourceType, creditedToUser, fraudStatus, createdAt, updatedAt\) |
|
||||
| `jobs` | json | List of jobs \(id, title, confidential, status, employmentType, locationId, departmentId, defaultInterviewPlanId, interviewPlanIds\[\], customFields\[\], jobPostingIds\[\], customRequisitionId, brandId, hiringTeam\[\], author, createdAt, updatedAt, openedAt, closedAt, location with address, openings\[\] with latestVersion, compensation with compensationTiers\[\]\) |
|
||||
| `applications` | json | List of applications \(id, status, customFields\[\], candidate summary, currentInterviewStage, source with sourceType, archiveReason with customFields\[\], archivedAt, job summary, creditedToUser, hiringTeam\[\], appliedViaJobPostingId, submitterClientIp, submitterUserAgent, createdAt, updatedAt\) |
|
||||
| `notes` | json | List of notes \(id, content, author, isPrivate, createdAt\) |
|
||||
| `offers` | json | List of offers \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion with id/startDate/salary/createdAt/openingId/customFields\[\]/fileHandles\[\]/author/approvalStatus\) |
|
||||
| `archiveReasons` | json | List of archive reasons \(id, text, reasonType \[RejectedByCandidate/RejectedByOrg/Other\], isArchived\) |
|
||||
| `sources` | json | List of sources \(id, title, isArchived, sourceType \{id, title, isArchived\}\) |
|
||||
| `customFields` | json | List of custom field definitions \(id, title, isPrivate, fieldType, objectType, isArchived, isRequired, selectableValues\[\] \{label, value, isArchived\}\) |
|
||||
| `departments` | json | List of departments \(id, name, externalName, isArchived, parentId, createdAt, updatedAt\) |
|
||||
| `locations` | json | List of locations \(id, name, externalName, isArchived, isRemote, workplaceType, parentLocationId, type, address with addressCountry/Region/Locality/postalCode/streetAddress\) |
|
||||
| `jobPostings` | json | List of job postings \(id, title, jobId, departmentName, teamName, locationName, locationIds, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensationTierSummary, shouldDisplayCompensationOnJobBoard, updatedAt\) |
|
||||
| `openings` | json | List of openings \(id, openedAt, closedAt, isArchived, archivedAt, closeReasonId, openingState, latestVersion with identifier/description/authorId/createdAt/teamId/jobIds\[\]/targetHireDate/targetStartDate/isBackfill/employmentType/locationIds\[\]/hiringTeam\[\]/customFields\[\]\) |
|
||||
| `users` | json | List of users \(id, firstName, lastName, email, globalRole, isEnabled, updatedAt, managerId\) |
|
||||
| `interviewSchedules` | json | List of interview schedules \(id, applicationId, interviewStageId, interviewEvents\[\] with interviewerUserIds/startTime/endTime/feedbackLink/location/meetingLink/hasSubmittedFeedback, status, scheduledBy, createdAt, updatedAt\) |
|
||||
| `tags` | json | List of candidate tags \(id, title, isArchived\) |
|
||||
| `id` | string | Resource UUID |
|
||||
| `name` | string | Resource name |
|
||||
| `title` | string | Job title or job posting title |
|
||||
| `status` | string | Status |
|
||||
| `candidate` | json | Candidate details \(id, name, primaryEmailAddress, primaryPhoneNumber, emailAddresses\[\], phoneNumbers\[\], socialLinks\[\], customFields\[\], source, creditedToUser, createdAt, updatedAt\) |
|
||||
| `job` | json | Job details \(id, title, status, employmentType, locationId, departmentId, hiringTeam\[\], author, location, openings\[\], compensation, createdAt, updatedAt\) |
|
||||
| `application` | json | Application details \(id, status, customFields\[\], candidate, currentInterviewStage, source, archiveReason, job, hiringTeam\[\], createdAt, updatedAt\) |
|
||||
| `offer` | json | Offer details \(id, decidedAt, applicationId, acceptanceStatus, offerStatus, latestVersion\) |
|
||||
| `jobPosting` | json | Job posting details \(id, title, descriptionPlain, descriptionHtml, descriptionSocial, descriptionParts, departmentName, teamName, teamNameHierarchy\[\], jobId, locationName, locationIds, linkedData, address, isRemote, workplaceType, employmentType, isListed, publishedDate, applicationDeadline, externalLink, applyLink, compensation, updatedAt\) |
|
||||
| `content` | string | Note content |
|
||||
| `author` | json | Note author \(id, firstName, lastName, email, globalRole, isEnabled\) |
|
||||
| `isPrivate` | boolean | Whether the note is private |
|
||||
| `id` | string | Candidate UUID |
|
||||
| `name` | string | Full name |
|
||||
| `primaryEmailAddress` | object | Primary email contact info |
|
||||
| ↳ `value` | string | Email address |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary email |
|
||||
| `primaryPhoneNumber` | object | Primary phone contact info |
|
||||
| ↳ `value` | string | Phone number |
|
||||
| ↳ `type` | string | Contact type \(Personal, Work, Other\) |
|
||||
| ↳ `isPrimary` | boolean | Whether this is the primary phone |
|
||||
| `profileUrl` | string | URL to the candidate Ashby profile |
|
||||
| `position` | string | Current position or title |
|
||||
| `company` | string | Current company |
|
||||
| `linkedInUrl` | string | LinkedIn profile URL |
|
||||
| `githubUrl` | string | GitHub profile URL |
|
||||
| `tags` | array | Tags applied to the candidate |
|
||||
| ↳ `id` | string | Tag UUID |
|
||||
| ↳ `title` | string | Tag title |
|
||||
| `applicationIds` | array | IDs of associated applications |
|
||||
| `createdAt` | string | ISO 8601 creation timestamp |
|
||||
| `moreDataAvailable` | boolean | Whether more pages exist |
|
||||
| `nextCursor` | string | Pagination cursor for next page |
|
||||
| `syncToken` | string | Sync token for incremental updates |
|
||||
| `updatedAt` | string | ISO 8601 last update timestamp |
|
||||
|
||||
|
||||
|
||||
@@ -57,12 +57,9 @@ Run a CloudWatch Log Insights query against one or more log groups
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Query result rows \(each row is a key/value map of field name to value\) |
|
||||
| `statistics` | object | Query statistics |
|
||||
| ↳ `bytesScanned` | number | Total bytes of log data scanned |
|
||||
| ↳ `recordsMatched` | number | Number of log records that matched the query |
|
||||
| ↳ `recordsScanned` | number | Total log records scanned |
|
||||
| `status` | string | Query completion status \(Complete, Failed, Cancelled, or Timeout\) |
|
||||
| `results` | array | Query result rows |
|
||||
| `statistics` | object | Query statistics \(bytesScanned, recordsMatched, recordsScanned\) |
|
||||
| `status` | string | Query completion status |
|
||||
|
||||
### `cloudwatch_describe_log_groups`
|
||||
|
||||
@@ -83,11 +80,6 @@ List available CloudWatch log groups
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `logGroups` | array | List of CloudWatch log groups with metadata |
|
||||
| ↳ `logGroupName` | string | Log group name |
|
||||
| ↳ `arn` | string | Log group ARN |
|
||||
| ↳ `storedBytes` | number | Total stored bytes |
|
||||
| ↳ `retentionInDays` | number | Retention period in days \(if set\) |
|
||||
| ↳ `creationTime` | number | Creation time in epoch milliseconds |
|
||||
|
||||
### `cloudwatch_get_log_events`
|
||||
|
||||
@@ -111,9 +103,6 @@ Retrieve log events from a specific CloudWatch log stream
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `events` | array | Log events with timestamp, message, and ingestion time |
|
||||
| ↳ `timestamp` | number | Event timestamp in epoch milliseconds |
|
||||
| ↳ `message` | string | Log event message |
|
||||
| ↳ `ingestionTime` | number | Ingestion time in epoch milliseconds |
|
||||
|
||||
### `cloudwatch_describe_log_streams`
|
||||
|
||||
@@ -134,12 +123,7 @@ List log streams within a CloudWatch log group
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `logStreams` | array | List of log streams with metadata, sorted by last event time \(most recent first\) unless a prefix filter is applied |
|
||||
| ↳ `logStreamName` | string | Log stream name |
|
||||
| ↳ `lastEventTimestamp` | number | Timestamp of the last log event in epoch milliseconds |
|
||||
| ↳ `firstEventTimestamp` | number | Timestamp of the first log event in epoch milliseconds |
|
||||
| ↳ `creationTime` | number | Stream creation time in epoch milliseconds |
|
||||
| ↳ `storedBytes` | number | Total stored bytes |
|
||||
| `logStreams` | array | List of log streams with metadata |
|
||||
|
||||
### `cloudwatch_list_metrics`
|
||||
|
||||
@@ -162,9 +146,6 @@ List available CloudWatch metrics
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metrics` | array | List of metrics with namespace, name, and dimensions |
|
||||
| ↳ `namespace` | string | Metric namespace \(e.g., AWS/EC2\) |
|
||||
| ↳ `metricName` | string | Metric name \(e.g., CPUUtilization\) |
|
||||
| ↳ `dimensions` | array | Array of name/value dimension pairs |
|
||||
|
||||
### `cloudwatch_get_metric_statistics`
|
||||
|
||||
@@ -189,15 +170,8 @@ Get statistics for a CloudWatch metric over a time range
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `label` | string | Metric label returned by CloudWatch |
|
||||
| `datapoints` | array | Datapoints sorted by timestamp with statistics values |
|
||||
| ↳ `timestamp` | number | Datapoint timestamp in epoch milliseconds |
|
||||
| ↳ `average` | number | Average statistic value |
|
||||
| ↳ `sum` | number | Sum statistic value |
|
||||
| ↳ `minimum` | number | Minimum statistic value |
|
||||
| ↳ `maximum` | number | Maximum statistic value |
|
||||
| ↳ `sampleCount` | number | Sample count statistic value |
|
||||
| ↳ `unit` | string | Unit of the metric |
|
||||
| `label` | string | Metric label |
|
||||
| `datapoints` | array | Datapoints with timestamp and statistics values |
|
||||
|
||||
### `cloudwatch_put_metric_data`
|
||||
|
||||
@@ -248,13 +222,5 @@ List and filter CloudWatch alarms
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `alarms` | array | List of CloudWatch alarms with state and configuration |
|
||||
| ↳ `alarmName` | string | Alarm name |
|
||||
| ↳ `alarmArn` | string | Alarm ARN |
|
||||
| ↳ `stateValue` | string | Current state \(OK, ALARM, INSUFFICIENT_DATA\) |
|
||||
| ↳ `stateReason` | string | Human-readable reason for the state |
|
||||
| ↳ `metricName` | string | Metric name \(MetricAlarm only\) |
|
||||
| ↳ `namespace` | string | Metric namespace \(MetricAlarm only\) |
|
||||
| ↳ `threshold` | number | Threshold value \(MetricAlarm only\) |
|
||||
| ↳ `stateUpdatedTimestamp` | number | Epoch ms when state last changed |
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Amazon DynamoDB
|
||||
description: Get, put, query, scan, update, and delete items in Amazon DynamoDB tables
|
||||
description: Connect to Amazon DynamoDB
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -55,7 +55,7 @@ Get an item from a DynamoDB table by primary key
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `key` | json | Yes | Primary key of the item to retrieve \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `key` | object | Yes | Primary key of the item to retrieve \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `consistentRead` | boolean | No | Use strongly consistent read |
|
||||
|
||||
#### Output
|
||||
@@ -63,7 +63,7 @@ Get an item from a DynamoDB table by primary key
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `item` | json | Retrieved item |
|
||||
| `item` | object | Retrieved item |
|
||||
|
||||
### `dynamodb_put`
|
||||
|
||||
@@ -77,17 +77,14 @@ Put an item into a DynamoDB table
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `item` | json | Yes | Item to put into the table \(e.g., \{"pk": "USER#123", "name": "John", "email": "john@example.com"\}\) |
|
||||
| `conditionExpression` | string | No | Condition that must be met for the put to succeed \(e.g., "attribute_not_exists\(pk\)" to prevent overwrites\) |
|
||||
| `expressionAttributeNames` | json | No | Attribute name mappings for reserved words used in conditionExpression \(e.g., \{"#name": "name"\}\) |
|
||||
| `expressionAttributeValues` | json | No | Expression attribute values used in conditionExpression \(e.g., \{":expected": "value"\}\) |
|
||||
| `item` | object | Yes | Item to put into the table \(e.g., \{"pk": "USER#123", "name": "John", "email": "john@example.com"\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `item` | json | Created item |
|
||||
| `item` | object | Created item |
|
||||
|
||||
### `dynamodb_query`
|
||||
|
||||
@@ -103,12 +100,10 @@ Query items from a DynamoDB table using key conditions
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `keyConditionExpression` | string | Yes | Key condition expression \(e.g., "pk = :pk" or "pk = :pk AND sk BEGINS_WITH :prefix"\) |
|
||||
| `filterExpression` | string | No | Filter expression for results \(e.g., "age > :minAge AND #status = :status"\) |
|
||||
| `expressionAttributeNames` | json | No | Attribute name mappings for reserved words \(e.g., \{"#status": "status"\}\) |
|
||||
| `expressionAttributeValues` | json | No | Expression attribute values \(e.g., \{":pk": "USER#123", ":minAge": 18\}\) |
|
||||
| `expressionAttributeNames` | object | No | Attribute name mappings for reserved words \(e.g., \{"#status": "status"\}\) |
|
||||
| `expressionAttributeValues` | object | No | Expression attribute values \(e.g., \{":pk": "USER#123", ":minAge": 18\}\) |
|
||||
| `indexName` | string | No | Secondary index name to query \(e.g., "GSI1", "email-index"\) |
|
||||
| `limit` | number | No | Maximum number of items to return \(e.g., 10, 50, 100\) |
|
||||
| `exclusiveStartKey` | json | No | Pagination token from a previous query's lastEvaluatedKey to continue fetching results |
|
||||
| `scanIndexForward` | boolean | No | Sort order for the sort key: true for ascending \(default\), false for descending |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -117,7 +112,6 @@ Query items from a DynamoDB table using key conditions
|
||||
| `message` | string | Operation status message |
|
||||
| `items` | array | Array of items returned |
|
||||
| `count` | number | Number of items returned |
|
||||
| `lastEvaluatedKey` | json | Pagination token to pass as exclusiveStartKey to fetch the next page of results |
|
||||
|
||||
### `dynamodb_scan`
|
||||
|
||||
@@ -133,10 +127,9 @@ Scan all items in a DynamoDB table
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `filterExpression` | string | No | Filter expression for results \(e.g., "age > :minAge AND #status = :status"\) |
|
||||
| `projectionExpression` | string | No | Attributes to retrieve \(e.g., "pk, sk, #name, email"\) |
|
||||
| `expressionAttributeNames` | json | No | Attribute name mappings for reserved words \(e.g., \{"#name": "name", "#status": "status"\}\) |
|
||||
| `expressionAttributeValues` | json | No | Expression attribute values \(e.g., \{":minAge": 18, ":status": "active"\}\) |
|
||||
| `expressionAttributeNames` | object | No | Attribute name mappings for reserved words \(e.g., \{"#name": "name", "#status": "status"\}\) |
|
||||
| `expressionAttributeValues` | object | No | Expression attribute values \(e.g., \{":minAge": 18, ":status": "active"\}\) |
|
||||
| `limit` | number | No | Maximum number of items to return \(e.g., 10, 50, 100\) |
|
||||
| `exclusiveStartKey` | json | No | Pagination token from a previous scan's lastEvaluatedKey to continue fetching results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -145,7 +138,6 @@ Scan all items in a DynamoDB table
|
||||
| `message` | string | Operation status message |
|
||||
| `items` | array | Array of items returned |
|
||||
| `count` | number | Number of items returned |
|
||||
| `lastEvaluatedKey` | json | Pagination token to pass as exclusiveStartKey to fetch the next page of results |
|
||||
|
||||
### `dynamodb_update`
|
||||
|
||||
@@ -159,10 +151,10 @@ Update an item in a DynamoDB table
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `key` | json | Yes | Primary key of the item to update \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `key` | object | Yes | Primary key of the item to update \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `updateExpression` | string | Yes | Update expression \(e.g., "SET #name = :name, age = :age" or "SET #count = #count + :inc"\) |
|
||||
| `expressionAttributeNames` | json | No | Attribute name mappings for reserved words \(e.g., \{"#name": "name", "#count": "count"\}\) |
|
||||
| `expressionAttributeValues` | json | No | Expression attribute values \(e.g., \{":name": "John", ":age": 30, ":inc": 1\}\) |
|
||||
| `expressionAttributeNames` | object | No | Attribute name mappings for reserved words \(e.g., \{"#name": "name", "#count": "count"\}\) |
|
||||
| `expressionAttributeValues` | object | No | Expression attribute values \(e.g., \{":name": "John", ":age": 30, ":inc": 1\}\) |
|
||||
| `conditionExpression` | string | No | Condition that must be met for the update to succeed \(e.g., "attribute_exists\(pk\)" or "version = :expectedVersion"\) |
|
||||
|
||||
#### Output
|
||||
@@ -170,7 +162,7 @@ Update an item in a DynamoDB table
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `item` | json | Updated item with all attributes |
|
||||
| `item` | object | Updated item |
|
||||
|
||||
### `dynamodb_delete`
|
||||
|
||||
@@ -184,10 +176,8 @@ Delete an item from a DynamoDB table
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `tableName` | string | Yes | DynamoDB table name \(e.g., "Users", "Orders"\) |
|
||||
| `key` | json | Yes | Primary key of the item to delete \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `key` | object | Yes | Primary key of the item to delete \(e.g., \{"pk": "USER#123"\} or \{"pk": "ORDER#456", "sk": "ITEM#789"\}\) |
|
||||
| `conditionExpression` | string | No | Condition that must be met for the delete to succeed \(e.g., "attribute_exists\(pk\)"\) |
|
||||
| `expressionAttributeNames` | json | No | Attribute name mappings for reserved words used in conditionExpression \(e.g., \{"#status": "status"\}\) |
|
||||
| `expressionAttributeValues` | json | No | Expression attribute values used in conditionExpression \(e.g., \{":status": "active"\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -214,6 +204,6 @@ Introspect DynamoDB to list tables or get detailed schema information for a spec
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `tables` | array | List of table names in the region |
|
||||
| `tableDetails` | json | Detailed schema information for a specific table |
|
||||
| `tableDetails` | object | Detailed schema information for a specific table |
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ Get detailed information about an IAM user
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `userName` | string | No | The name of the IAM user to retrieve \(defaults to the caller if omitted\) |
|
||||
| `userName` | string | Yes | The name of the IAM user to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -440,80 +440,4 @@ Remove an IAM user from a group
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `iam_list_attached_role_policies`
|
||||
|
||||
List all managed policies attached to an IAM role
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `roleName` | string | Yes | Name of the IAM role |
|
||||
| `pathPrefix` | string | No | Path prefix to filter policies \(e.g., /application/\) |
|
||||
| `maxItems` | number | No | Maximum number of policies to return \(1-1000\) |
|
||||
| `marker` | string | No | Pagination marker from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `attachedPolicies` | json | List of attached policies with policyName and policyArn |
|
||||
| `isTruncated` | boolean | Whether there are more results available |
|
||||
| `marker` | string | Pagination marker for the next page of results |
|
||||
| `count` | number | Number of attached policies returned |
|
||||
|
||||
### `iam_list_attached_user_policies`
|
||||
|
||||
List all managed policies attached to an IAM user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `userName` | string | Yes | Name of the IAM user |
|
||||
| `pathPrefix` | string | No | Path prefix to filter policies \(e.g., /application/\) |
|
||||
| `maxItems` | number | No | Maximum number of policies to return \(1-1000\) |
|
||||
| `marker` | string | No | Pagination marker from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `attachedPolicies` | json | List of attached policies with policyName and policyArn |
|
||||
| `isTruncated` | boolean | Whether there are more results available |
|
||||
| `marker` | string | Pagination marker for the next page of results |
|
||||
| `count` | number | Number of attached policies returned |
|
||||
|
||||
### `iam_simulate_principal_policy`
|
||||
|
||||
Simulate whether a user, role, or group is allowed to perform specific AWS actions — useful for pre-flight access checks
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `policySourceArn` | string | Yes | ARN of the user, group, or role to simulate \(e.g., arn:aws:iam::123456789012:user/alice\) |
|
||||
| `actionNames` | string | Yes | Comma-separated list of AWS actions to simulate \(e.g., s3:GetObject,ec2:DescribeInstances\) |
|
||||
| `resourceArns` | string | No | Comma-separated list of resource ARNs to simulate against \(defaults to * if not provided\) |
|
||||
| `maxResults` | number | No | Maximum number of simulation results to return \(1-1000\) |
|
||||
| `marker` | string | No | Pagination marker from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `evaluationResults` | json | Simulation results per action: evalActionName, evalResourceName, evalDecision \(allowed/explicitDeny/implicitDeny\), matchedStatements \(sourcePolicyId, sourcePolicyType\), missingContextValues |
|
||||
| `isTruncated` | boolean | Whether there are more results available |
|
||||
| `marker` | string | Pagination marker for the next page of results |
|
||||
| `count` | number | Number of evaluation results returned |
|
||||
|
||||
|
||||
|
||||
@@ -1,340 +0,0 @@
|
||||
---
|
||||
title: AWS Identity Center
|
||||
description: Manage temporary elevated access in AWS IAM Identity Center
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="identity_center"
|
||||
color="linear-gradient(45deg, #BD0816 0%, #FF5252 100%)"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/) (formerly AWS Single Sign-On) is the recommended service for managing workforce access to multiple AWS accounts and applications. It provides a central place to assign users and groups temporary, permission-scoped access to AWS accounts using permission sets — without creating long-lived IAM credentials.
|
||||
|
||||
With AWS IAM Identity Center, you can:
|
||||
|
||||
- **Provision account assignments**: Grant a user or group access to a specific AWS account with a specific permission set — the core primitive of temporary elevated access
|
||||
- **Revoke access on demand**: Delete account assignments to immediately remove elevated permissions when they are no longer needed
|
||||
- **Look up users by email**: Resolve a federated identity (email address) to an Identity Store user ID for programmatic access provisioning
|
||||
- **List permission sets**: Enumerate the available permission sets (e.g., ReadOnly, PowerUser, AdministratorAccess) defined in your Identity Center instance
|
||||
- **Monitor assignment status**: Poll the provisioning status of create/delete operations, which are asynchronous in AWS
|
||||
- **List accounts in your organization**: Enumerate all AWS accounts in your AWS Organizations structure to populate access request dropdowns
|
||||
- **Manage groups**: List groups and resolve group IDs by display name for group-based access grants
|
||||
|
||||
In Sim, the AWS Identity Center integration is designed to power **TEAM (Temporary Elevated Access Management)** workflows — automated pipelines where users request elevated access, approvers approve or deny it, access is provisioned with a time limit, and auto-revocation removes it when the window expires. This replaces manual console-based access management with auditable, agent-driven workflows that integrate with Slack, email, ticketing systems, and CloudTrail for full traceability.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Provision and revoke temporary access to AWS accounts via IAM Identity Center (SSO). Assign permission sets to users or groups, look up users by email, and list accounts and permission sets for access request workflows.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `identity_center_list_instances`
|
||||
|
||||
List all AWS IAM Identity Center instances in your account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `maxResults` | number | No | Maximum number of instances to return \(1-100\) |
|
||||
| `nextToken` | string | No | Pagination token from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `instances` | json | List of Identity Center instances with instanceArn, identityStoreId, name, status, statusReason |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of instances returned |
|
||||
|
||||
### `identity_center_list_accounts`
|
||||
|
||||
List all AWS accounts in your organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `maxResults` | number | No | Maximum number of accounts to return |
|
||||
| `nextToken` | string | No | Pagination token from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `accounts` | json | List of AWS accounts with id, arn, name, email, status |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of accounts returned |
|
||||
|
||||
### `identity_center_describe_account`
|
||||
|
||||
Retrieve details about a specific AWS account by its ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `accountId` | string | Yes | AWS account ID to describe |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | AWS account ID |
|
||||
| `arn` | string | AWS account ARN |
|
||||
| `name` | string | Account name |
|
||||
| `email` | string | Root email address of the account |
|
||||
| `status` | string | Account status \(ACTIVE, SUSPENDED, etc.\) |
|
||||
| `joinedTimestamp` | string | Date the account joined the organization |
|
||||
|
||||
### `identity_center_list_permission_sets`
|
||||
|
||||
List all permission sets defined in an IAM Identity Center instance
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `maxResults` | number | No | Maximum number of permission sets to return |
|
||||
| `nextToken` | string | No | Pagination token from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `permissionSets` | json | List of permission sets with permissionSetArn, name, description, sessionDuration |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of permission sets returned |
|
||||
|
||||
### `identity_center_get_user`
|
||||
|
||||
Look up a user in the Identity Store by email address
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `identityStoreId` | string | Yes | Identity Store ID \(from the Identity Center instance\) |
|
||||
| `email` | string | Yes | Email address of the user to look up |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Identity Store user ID \(use as principalId\) |
|
||||
| `userName` | string | Username in the Identity Store |
|
||||
| `displayName` | string | Display name of the user |
|
||||
| `email` | string | Email address of the user |
|
||||
|
||||
### `identity_center_get_group`
|
||||
|
||||
Look up a group in the Identity Store by display name
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `identityStoreId` | string | Yes | Identity Store ID \(from the Identity Center instance\) |
|
||||
| `displayName` | string | Yes | Display name of the group to look up |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groupId` | string | Identity Store group ID \(use as principalId\) |
|
||||
| `displayName` | string | Display name of the group |
|
||||
| `description` | string | Group description |
|
||||
|
||||
### `identity_center_list_groups`
|
||||
|
||||
List all groups in the Identity Store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `identityStoreId` | string | Yes | Identity Store ID \(from the Identity Center instance\) |
|
||||
| `maxResults` | number | No | Maximum number of groups to return |
|
||||
| `nextToken` | string | No | Pagination token from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groups` | json | List of groups with groupId, displayName, description |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of groups returned |
|
||||
|
||||
### `identity_center_create_account_assignment`
|
||||
|
||||
Grant a user or group access to an AWS account via a permission set (temporary elevated access)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `accountId` | string | Yes | AWS account ID to grant access to |
|
||||
| `permissionSetArn` | string | Yes | ARN of the permission set to assign |
|
||||
| `principalType` | string | Yes | Type of principal: USER or GROUP |
|
||||
| `principalId` | string | Yes | Identity Store ID of the user or group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Status message |
|
||||
| `status` | string | Provisioning status: IN_PROGRESS, FAILED, or SUCCEEDED |
|
||||
| `requestId` | string | Request ID to use with Check Assignment Status |
|
||||
| `accountId` | string | Target AWS account ID |
|
||||
| `permissionSetArn` | string | Permission set ARN |
|
||||
| `principalType` | string | Principal type \(USER or GROUP\) |
|
||||
| `principalId` | string | Principal ID |
|
||||
| `failureReason` | string | Reason for failure if status is FAILED |
|
||||
| `createdDate` | string | Date the request was created |
|
||||
|
||||
### `identity_center_delete_account_assignment`
|
||||
|
||||
Revoke a user or group access to an AWS account by removing a permission set assignment
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `accountId` | string | Yes | AWS account ID to revoke access from |
|
||||
| `permissionSetArn` | string | Yes | ARN of the permission set to remove |
|
||||
| `principalType` | string | Yes | Type of principal: USER or GROUP |
|
||||
| `principalId` | string | Yes | Identity Store ID of the user or group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Status message |
|
||||
| `status` | string | Deprovisioning status: IN_PROGRESS, FAILED, or SUCCEEDED |
|
||||
| `requestId` | string | Request ID to use with Check Assignment Status |
|
||||
| `accountId` | string | Target AWS account ID |
|
||||
| `permissionSetArn` | string | Permission set ARN |
|
||||
| `principalType` | string | Principal type \(USER or GROUP\) |
|
||||
| `principalId` | string | Principal ID |
|
||||
| `failureReason` | string | Reason for failure if status is FAILED |
|
||||
| `createdDate` | string | Date the request was created |
|
||||
|
||||
### `identity_center_check_assignment_status`
|
||||
|
||||
Check the provisioning status of an account assignment creation request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `requestId` | string | Yes | Request ID returned from Create or Delete Account Assignment |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Human-readable status message |
|
||||
| `status` | string | Current status: IN_PROGRESS, FAILED, or SUCCEEDED |
|
||||
| `requestId` | string | The request ID that was checked |
|
||||
| `accountId` | string | Target AWS account ID |
|
||||
| `permissionSetArn` | string | Permission set ARN |
|
||||
| `principalType` | string | Principal type \(USER or GROUP\) |
|
||||
| `principalId` | string | Principal ID |
|
||||
| `failureReason` | string | Reason for failure if status is FAILED |
|
||||
| `createdDate` | string | Date the request was created |
|
||||
|
||||
### `identity_center_check_assignment_deletion_status`
|
||||
|
||||
Check the deprovisioning status of an account assignment deletion request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `requestId` | string | Yes | Request ID returned from Delete Account Assignment |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Human-readable status message |
|
||||
| `status` | string | Current deletion status: IN_PROGRESS, FAILED, or SUCCEEDED |
|
||||
| `requestId` | string | The deletion request ID that was checked |
|
||||
| `accountId` | string | Target AWS account ID |
|
||||
| `permissionSetArn` | string | Permission set ARN |
|
||||
| `principalType` | string | Principal type \(USER or GROUP\) |
|
||||
| `principalId` | string | Principal ID |
|
||||
| `failureReason` | string | Reason for failure if status is FAILED |
|
||||
| `createdDate` | string | Date the request was created |
|
||||
|
||||
### `identity_center_list_account_assignments`
|
||||
|
||||
List all account assignments for a specific user or group across all accounts
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
|
||||
| `principalId` | string | Yes | Identity Store ID of the user or group |
|
||||
| `principalType` | string | Yes | Type of principal: USER or GROUP |
|
||||
| `maxResults` | number | No | Maximum number of assignments to return |
|
||||
| `nextToken` | string | No | Pagination token from a previous request |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `assignments` | json | List of account assignments with accountId, permissionSetArn, principalType, principalId |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of assignments returned |
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"index",
|
||||
"a2a",
|
||||
"agentmail",
|
||||
"agentphone",
|
||||
"agiloft",
|
||||
"ahrefs",
|
||||
"airtable",
|
||||
@@ -87,7 +86,6 @@
|
||||
"huggingface",
|
||||
"hunter",
|
||||
"iam",
|
||||
"identity_center",
|
||||
"image_generator",
|
||||
"imap",
|
||||
"incidentio",
|
||||
@@ -117,7 +115,6 @@
|
||||
"microsoft_planner",
|
||||
"microsoft_teams",
|
||||
"mistral_parse",
|
||||
"monday",
|
||||
"mongodb",
|
||||
"mysql",
|
||||
"neo4j",
|
||||
@@ -156,7 +153,6 @@
|
||||
"sentry",
|
||||
"serper",
|
||||
"servicenow",
|
||||
"ses",
|
||||
"sftp",
|
||||
"sharepoint",
|
||||
"shopify",
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
---
|
||||
title: Monday
|
||||
description: Manage Monday.com boards, items, and groups
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="monday"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate with Monday.com to list boards, get board details, fetch and search items, create and update items, archive or delete items, create subitems, move items between groups, add updates, and create groups.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `monday_list_boards`
|
||||
|
||||
List boards from your Monday.com account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | number | No | Maximum number of boards to return \(default 25, max 500\) |
|
||||
| `page` | number | No | Page number for pagination \(starts at 1\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boards` | array | List of Monday.com boards |
|
||||
| ↳ `id` | string | Board ID |
|
||||
| ↳ `name` | string | Board name |
|
||||
| ↳ `description` | string | Board description |
|
||||
| ↳ `state` | string | Board state \(active, archived, deleted\) |
|
||||
| ↳ `boardKind` | string | Board kind \(public, private, share\) |
|
||||
| ↳ `itemsCount` | number | Number of items on the board |
|
||||
| ↳ `url` | string | Board URL |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| `count` | number | Number of boards returned |
|
||||
|
||||
### `monday_get_board`
|
||||
|
||||
Get a specific Monday.com board with its groups and columns
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `board` | json | Board details |
|
||||
| ↳ `id` | string | Board ID |
|
||||
| ↳ `name` | string | Board name |
|
||||
| ↳ `description` | string | Board description |
|
||||
| ↳ `state` | string | Board state |
|
||||
| ↳ `boardKind` | string | Board kind \(public, private, share\) |
|
||||
| ↳ `itemsCount` | number | Number of items |
|
||||
| ↳ `url` | string | Board URL |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| `groups` | array | Groups on the board |
|
||||
| ↳ `id` | string | Group ID |
|
||||
| ↳ `title` | string | Group title |
|
||||
| ↳ `color` | string | Group color \(hex\) |
|
||||
| ↳ `archived` | boolean | Whether the group is archived |
|
||||
| ↳ `deleted` | boolean | Whether the group is deleted |
|
||||
| ↳ `position` | string | Group position |
|
||||
| `columns` | array | Columns on the board |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `title` | string | Column title |
|
||||
| ↳ `type` | string | Column type |
|
||||
|
||||
### `monday_get_item`
|
||||
|
||||
Get a specific item by ID from Monday.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `itemId` | string | Yes | The ID of the item to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The requested item |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
|
||||
### `monday_get_items`
|
||||
|
||||
Get items from a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board to get items from |
|
||||
| `groupId` | string | No | Filter items by group ID |
|
||||
| `limit` | number | No | Maximum number of items to return \(default 25, max 500\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | List of items from the board |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state \(active, archived, deleted\) |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values for the item |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Human-readable text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
| `count` | number | Number of items returned |
|
||||
|
||||
### `monday_search_items`
|
||||
|
||||
Search for items on a Monday.com board by column values
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board to search |
|
||||
| `columns` | string | Yes | JSON array of column filters, e.g. \[\{"column_id":"status","column_values":\["Done"\]\}\] |
|
||||
| `limit` | number | No | Maximum number of items to return \(default 25, max 500\) |
|
||||
| `cursor` | string | No | Pagination cursor from a previous search response |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Matching items |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
| `count` | number | Number of items returned |
|
||||
| `cursor` | string | Pagination cursor for fetching the next page |
|
||||
|
||||
### `monday_create_item`
|
||||
|
||||
Create a new item on a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board to create the item on |
|
||||
| `itemName` | string | Yes | The name of the new item |
|
||||
| `groupId` | string | No | The group ID to create the item in |
|
||||
| `columnValues` | string | No | JSON string of column values to set \(e.g., \{"status":"Done","date":"2024-01-01"\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The created item |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
|
||||
### `monday_update_item`
|
||||
|
||||
Update column values of an item on a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board containing the item |
|
||||
| `itemId` | string | Yes | The ID of the item to update |
|
||||
| `columnValues` | string | Yes | JSON string of column values to update \(e.g., \{"status":"Done","date":"2024-01-01"\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The updated item |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
|
||||
### `monday_delete_item`
|
||||
|
||||
Delete an item from a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `itemId` | string | Yes | The ID of the item to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | The ID of the deleted item |
|
||||
|
||||
### `monday_archive_item`
|
||||
|
||||
Archive an item on a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `itemId` | string | Yes | The ID of the item to archive |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | The ID of the archived item |
|
||||
|
||||
### `monday_move_item_to_group`
|
||||
|
||||
Move an item to a different group on a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `itemId` | string | Yes | The ID of the item to move |
|
||||
| `groupId` | string | Yes | The ID of the target group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The moved item with updated group |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
|
||||
### `monday_create_subitem`
|
||||
|
||||
Create a subitem under a parent item on Monday.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `parentItemId` | string | Yes | The ID of the parent item |
|
||||
| `itemName` | string | Yes | The name of the new subitem |
|
||||
| `columnValues` | string | No | JSON string of column values to set |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The created subitem |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `name` | string | Item name |
|
||||
| ↳ `state` | string | Item state |
|
||||
| ↳ `boardId` | string | Board ID |
|
||||
| ↳ `groupId` | string | Group ID |
|
||||
| ↳ `groupTitle` | string | Group title |
|
||||
| ↳ `columnValues` | array | Column values |
|
||||
| ↳ `id` | string | Column ID |
|
||||
| ↳ `text` | string | Text value |
|
||||
| ↳ `value` | string | Raw JSON value |
|
||||
| ↳ `type` | string | Column type |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last updated timestamp |
|
||||
| ↳ `url` | string | Item URL |
|
||||
|
||||
### `monday_create_update`
|
||||
|
||||
Add an update (comment) to a Monday.com item
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `itemId` | string | Yes | The ID of the item to add the update to |
|
||||
| `body` | string | Yes | The update text content \(supports HTML\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `update` | json | The created update |
|
||||
| ↳ `id` | string | Update ID |
|
||||
| ↳ `body` | string | Update body \(HTML\) |
|
||||
| ↳ `textBody` | string | Plain text body |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `creatorId` | string | Creator user ID |
|
||||
| ↳ `itemId` | string | Item ID |
|
||||
|
||||
### `monday_create_group`
|
||||
|
||||
Create a new group on a Monday.com board
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `boardId` | string | Yes | The ID of the board to create the group on |
|
||||
| `groupName` | string | Yes | The name of the new group \(max 255 characters\) |
|
||||
| `groupColor` | string | No | The group color as a hex code \(e.g., "#ff642e"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `group` | json | The created group |
|
||||
| ↳ `id` | string | Group ID |
|
||||
| ↳ `title` | string | Group title |
|
||||
| ↳ `color` | string | Group color \(hex\) |
|
||||
| ↳ `archived` | boolean | Whether archived |
|
||||
| ↳ `deleted` | boolean | Whether deleted |
|
||||
| ↳ `position` | string | Group position |
|
||||
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
---
|
||||
title: AWS SES
|
||||
description: Send emails and manage templates with AWS Simple Email Service
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="ses"
|
||||
color="linear-gradient(45deg, #BD0816 0%, #FF5252 100%)"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Amazon Simple Email Service (SES)](https://aws.amazon.com/ses/) is a cloud-based email sending service designed for high-volume, transactional, and marketing email delivery. It provides a cost-effective, scalable way to send email without managing your own mail server infrastructure.
|
||||
|
||||
With AWS SES, you can:
|
||||
|
||||
- **Send simple emails**: Deliver one-off emails with plain text or HTML body content to individual recipients
|
||||
- **Send templated emails**: Use pre-defined templates with variable substitution (e.g., `{{name}}`, `{{link}}`) for personalized emails at scale
|
||||
- **Send bulk emails**: Deliver templated emails to large lists of recipients in a single API call, with per-destination data overrides
|
||||
- **Manage email templates**: Create, retrieve, list, and delete reusable email templates for transactional and marketing campaigns
|
||||
- **Monitor account health**: Retrieve your account's sending quota, send rate, and whether sending is currently enabled
|
||||
|
||||
In Sim, the AWS SES integration is designed for workflows that need reliable, programmatic email delivery — from access request notifications and approval alerts to bulk outreach and automated reporting. It pairs naturally with the IAM Identity Center integration for TEAM (Temporary Elevated Access Management) workflows, where email notifications are sent when access is provisioned, approved, or revoked.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate AWS SES v2 into the workflow. Send simple, templated, and bulk emails. Manage email templates and retrieve account sending quota and verified identity information.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `ses_send_email`
|
||||
|
||||
Send an email via AWS SES using simple or HTML content
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `fromAddress` | string | Yes | Verified sender email address |
|
||||
| `toAddresses` | string | Yes | Comma-separated list of recipient email addresses |
|
||||
| `subject` | string | Yes | Email subject line |
|
||||
| `bodyText` | string | No | Plain text email body |
|
||||
| `bodyHtml` | string | No | HTML email body |
|
||||
| `ccAddresses` | string | No | Comma-separated list of CC email addresses |
|
||||
| `bccAddresses` | string | No | Comma-separated list of BCC email addresses |
|
||||
| `replyToAddresses` | string | No | Comma-separated list of reply-to email addresses |
|
||||
| `configurationSetName` | string | No | SES configuration set name for tracking |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `messageId` | string | SES message ID for the sent email |
|
||||
|
||||
### `ses_send_templated_email`
|
||||
|
||||
Send an email using an SES email template with dynamic template data
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `fromAddress` | string | Yes | Verified sender email address |
|
||||
| `toAddresses` | string | Yes | Comma-separated list of recipient email addresses |
|
||||
| `templateName` | string | Yes | Name of the SES email template to use |
|
||||
| `templateData` | string | Yes | JSON string of key-value pairs for template variable substitution |
|
||||
| `ccAddresses` | string | No | Comma-separated list of CC email addresses |
|
||||
| `bccAddresses` | string | No | Comma-separated list of BCC email addresses |
|
||||
| `configurationSetName` | string | No | SES configuration set name for tracking |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `messageId` | string | SES message ID for the sent email |
|
||||
|
||||
### `ses_send_bulk_email`
|
||||
|
||||
Send emails to multiple recipients using an SES template with per-recipient data
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `fromAddress` | string | Yes | Verified sender email address |
|
||||
| `templateName` | string | Yes | Name of the SES email template to use |
|
||||
| `destinations` | string | Yes | JSON array of destination objects with toAddresses \(string\[\]\) and optional templateData \(JSON string\); falls back to defaultTemplateData when omitted |
|
||||
| `defaultTemplateData` | string | No | Default JSON template data used when a destination does not specify its own |
|
||||
| `configurationSetName` | string | No | SES configuration set name for tracking |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Per-destination send results with status and messageId |
|
||||
| `successCount` | number | Number of successfully sent emails |
|
||||
| `failureCount` | number | Number of failed email sends |
|
||||
|
||||
### `ses_list_identities`
|
||||
|
||||
List all verified email identities (email addresses and domains) in your SES account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `pageSize` | number | No | Maximum number of identities to return \(1-1000\) |
|
||||
| `nextToken` | string | No | Pagination token from a previous list response |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `identities` | array | List of email identities with name, type, sending status, and verification status |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of identities returned |
|
||||
|
||||
### `ses_get_account`
|
||||
|
||||
Get SES account sending quota and status information
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sendingEnabled` | boolean | Whether email sending is enabled for the account |
|
||||
| `max24HourSend` | number | Maximum emails allowed per 24-hour period |
|
||||
| `maxSendRate` | number | Maximum emails allowed per second |
|
||||
| `sentLast24Hours` | number | Number of emails sent in the last 24 hours |
|
||||
|
||||
### `ses_create_template`
|
||||
|
||||
Create a new SES email template for use with templated email sending
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `templateName` | string | Yes | Unique name for the email template |
|
||||
| `subjectPart` | string | Yes | Subject line template \(supports \{\{variable\}\} substitution\) |
|
||||
| `textPart` | string | No | Plain text version of the template body |
|
||||
| `htmlPart` | string | No | HTML version of the template body |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message for the created template |
|
||||
|
||||
### `ses_get_template`
|
||||
|
||||
Retrieve the content and details of an SES email template
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `templateName` | string | Yes | Name of the template to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `templateName` | string | Name of the template |
|
||||
| `subjectPart` | string | Subject line of the template |
|
||||
| `textPart` | string | Plain text body of the template |
|
||||
| `htmlPart` | string | HTML body of the template |
|
||||
|
||||
### `ses_list_templates`
|
||||
|
||||
List all SES email templates in your account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `pageSize` | number | No | Maximum number of templates to return |
|
||||
| `nextToken` | string | No | Pagination token from a previous list response |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `templates` | array | List of email templates with name and creation timestamp |
|
||||
| `nextToken` | string | Pagination token for the next page of results |
|
||||
| `count` | number | Number of templates returned |
|
||||
|
||||
### `ses_delete_template`
|
||||
|
||||
Delete an existing SES email template
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
|
||||
| `accessKeyId` | string | Yes | AWS access key ID |
|
||||
| `secretAccessKey` | string | Yes | AWS secret access key |
|
||||
| `templateName` | string | Yes | Name of the template to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message for the deleted template |
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ Assume an IAM role and receive temporary security credentials
|
||||
| `roleArn` | string | Yes | ARN of the IAM role to assume |
|
||||
| `roleSessionName` | string | Yes | Identifier for the assumed role session |
|
||||
| `durationSeconds` | number | No | Duration of the session in seconds \(900-43200, default 3600\) |
|
||||
| `policy` | string | No | JSON IAM policy to further restrict session permissions \(max 2048 chars\) |
|
||||
| `externalId` | string | No | External ID for cross-account access |
|
||||
| `serialNumber` | string | No | MFA device serial number or ARN |
|
||||
| `tokenCode` | string | No | MFA token code \(6 digits\) |
|
||||
@@ -62,7 +61,6 @@ Assume an IAM role and receive temporary security credentials
|
||||
| `assumedRoleArn` | string | ARN of the assumed role |
|
||||
| `assumedRoleId` | string | Assumed role ID with session name |
|
||||
| `packedPolicySize` | number | Percentage of allowed policy size used |
|
||||
| `sourceIdentity` | string | Source identity set on the role session, if any |
|
||||
|
||||
### `sts_get_caller_identity`
|
||||
|
||||
|
||||
@@ -97,14 +97,6 @@ Trigger workflow when a candidate is hired
|
||||
| ↳ `job` | object | job output from the tool |
|
||||
| ↳ `id` | string | Job UUID |
|
||||
| ↳ `title` | string | Job title |
|
||||
| `offer` | object | offer output from the tool |
|
||||
| ↳ `id` | string | Accepted offer UUID |
|
||||
| ↳ `applicationId` | string | Associated application UUID |
|
||||
| ↳ `acceptanceStatus` | string | Offer acceptance status |
|
||||
| ↳ `offerStatus` | string | Offer process status |
|
||||
| ↳ `decidedAt` | string | Offer decision timestamp \(ISO 8601\) |
|
||||
| ↳ `latestVersion` | object | latestVersion output from the tool |
|
||||
| ↳ `id` | string | Latest offer version UUID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -10,7 +10,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
Confluence provides 23 triggers for automating workflows based on events.
|
||||
Confluence provides 16 triggers for automating workflows based on events.
|
||||
|
||||
## Triggers
|
||||
|
||||
@@ -98,49 +98,6 @@ Trigger workflow when an attachment is removed in Confluence
|
||||
| `files` | file[] | Attachment file content downloaded from Confluence \(if includeFileContent is enabled with credentials\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Attachment Updated
|
||||
|
||||
Trigger workflow when an attachment is updated in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
| `confluenceEmail` | string | No | Your Atlassian account email. Required together with API token to download attachment files. |
|
||||
| `confluenceApiToken` | string | No | API token from https://id.atlassian.com/manage-profile/security/api-tokens. Required to download attachment file content. |
|
||||
| `includeFileContent` | boolean | No | Download and include actual file content from attachments. Requires email, API token, and domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
| `id` | number | Content ID |
|
||||
| `title` | string | Content title |
|
||||
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
|
||||
| `version` | number | Version number |
|
||||
| `spaceKey` | string | Space key the content belongs to |
|
||||
| `creatorAccountId` | string | Account ID of the creator |
|
||||
| `lastModifierAccountId` | string | Account ID of the last modifier |
|
||||
| `self` | string | URL link to the content |
|
||||
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
|
||||
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
|
||||
| `attachment` | object | attachment output from the tool |
|
||||
| ↳ `mediaType` | string | MIME type of the attachment |
|
||||
| ↳ `fileSize` | number | File size in bytes |
|
||||
| ↳ `parent` | object | parent output from the tool |
|
||||
| ↳ `id` | number | Container page/blog ID |
|
||||
| ↳ `title` | string | Container page/blog title |
|
||||
| ↳ `contentType` | string | Container content type |
|
||||
| `files` | file[] | Attachment file content downloaded from Confluence \(if includeFileContent is enabled with credentials\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Blog Post Created
|
||||
@@ -185,28 +142,6 @@ Trigger workflow when a blog post is removed in Confluence
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Blog Post Restored
|
||||
|
||||
Trigger workflow when a blog post is restored from trash in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Blog Post Updated
|
||||
@@ -307,45 +242,6 @@ Trigger workflow when a comment is removed in Confluence
|
||||
| ↳ `self` | string | URL link to the parent content |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Comment Updated
|
||||
|
||||
Trigger workflow when a comment is updated in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
| `id` | number | Content ID |
|
||||
| `title` | string | Content title |
|
||||
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
|
||||
| `version` | number | Version number |
|
||||
| `spaceKey` | string | Space key the content belongs to |
|
||||
| `creatorAccountId` | string | Account ID of the creator |
|
||||
| `lastModifierAccountId` | string | Account ID of the last modifier |
|
||||
| `self` | string | URL link to the content |
|
||||
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
|
||||
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `parent` | object | parent output from the tool |
|
||||
| ↳ `id` | number | Parent page/blog ID |
|
||||
| ↳ `title` | string | Parent page/blog title |
|
||||
| ↳ `contentType` | string | Parent content type \(page or blogpost\) |
|
||||
| ↳ `spaceKey` | string | Space key of the parent |
|
||||
| ↳ `self` | string | URL link to the parent content |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Label Added
|
||||
@@ -450,40 +346,6 @@ Trigger workflow when a page is moved in Confluence
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Page Permissions Updated
|
||||
|
||||
Trigger workflow when page permissions are changed in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
| `id` | number | Content ID |
|
||||
| `title` | string | Content title |
|
||||
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
|
||||
| `version` | number | Version number |
|
||||
| `spaceKey` | string | Space key the content belongs to |
|
||||
| `creatorAccountId` | string | Account ID of the creator |
|
||||
| `lastModifierAccountId` | string | Account ID of the last modifier |
|
||||
| `self` | string | URL link to the content |
|
||||
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
|
||||
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
|
||||
| `page` | object | page output from the tool |
|
||||
| ↳ `permissions` | json | Updated permissions object for the page |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Page Removed
|
||||
@@ -506,28 +368,6 @@ Trigger workflow when a page is removed or trashed in Confluence
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Page Restored
|
||||
|
||||
Trigger workflow when a page is restored from trash in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Page Updated
|
||||
@@ -576,32 +416,6 @@ Trigger workflow when a new space is created in Confluence
|
||||
| ↳ `self` | string | URL link to the space |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Space Removed
|
||||
|
||||
Trigger workflow when a space is removed in Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
| `space` | object | space output from the tool |
|
||||
| ↳ `key` | string | Space key |
|
||||
| ↳ `name` | string | Space name |
|
||||
| ↳ `self` | string | URL link to the space |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Space Updated
|
||||
@@ -628,35 +442,6 @@ Trigger workflow when a space is updated in Confluence
|
||||
| ↳ `self` | string | URL link to the space |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence User Created
|
||||
|
||||
Trigger workflow when a new user is added to Confluence
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
|
||||
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
|
||||
| `userAccountId` | string | Account ID of the user who triggered the event |
|
||||
| `accountType` | string | Account type \(e.g., customer\) |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `accountId` | string | Account ID of the new user |
|
||||
| ↳ `accountType` | string | Account type \(e.g., atlassian, app\) |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `emailAddress` | string | Email address of the user \(may not be available due to GDPR/privacy settings\) |
|
||||
| ↳ `publicName` | string | Public name of the user |
|
||||
| ↳ `self` | string | URL link to the user profile |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Confluence Webhook (All Events)
|
||||
@@ -687,6 +472,5 @@ Trigger workflow on any Confluence webhook event
|
||||
| `space` | json | Space object \(present in space events\) |
|
||||
| `label` | json | Label object \(present in label events\) |
|
||||
| `content` | json | Content object \(present in label events\) |
|
||||
| `user` | json | User object \(present in user events\) |
|
||||
| `files` | file[] | Attachment file content \(present in attachment events when includeFileContent is enabled\) |
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ Trigger workflow when a Fireflies meeting transcription is complete
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meetingId` | string | The ID of the transcribed meeting |
|
||||
| `eventType` | string | The type of event \(e.g. Transcription completed, meeting.transcribed\) |
|
||||
| `eventType` | string | The type of event \(Transcription completed\) |
|
||||
| `clientReferenceId` | string | Custom reference ID if set during upload |
|
||||
| `timestamp` | number | Unix timestamp in milliseconds when the event was fired \(V2 webhooks\) |
|
||||
|
||||
|
||||
@@ -10,182 +10,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
Jira provides 15 triggers for automating workflows based on events.
|
||||
Jira provides 6 triggers for automating workflows based on events.
|
||||
|
||||
## Triggers
|
||||
|
||||
### Jira Comment Deleted
|
||||
|
||||
Trigger workflow when a comment is deleted from a Jira issue
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which comment deletions trigger this workflow using JQL |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `votes` | json | Votes on this issue |
|
||||
| ↳ `labels` | array | Array of labels applied to this issue |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `created` | string | Issue creation date \(ISO format\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `progress` | json | Progress tracking information |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | json | Comment body in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Comment author display name |
|
||||
| ↳ `accountId` | string | Comment author account ID |
|
||||
| ↳ `emailAddress` | string | Comment author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the comment |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the comment |
|
||||
| ↳ `created` | string | Comment creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Comment last updated date \(ISO format\) |
|
||||
| ↳ `self` | string | REST API URL for this comment |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Comment Updated
|
||||
|
||||
Trigger workflow when a comment is updated on a Jira issue
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which comment updates trigger this workflow using JQL |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `votes` | json | Votes on this issue |
|
||||
| ↳ `labels` | array | Array of labels applied to this issue |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `created` | string | Issue creation date \(ISO format\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `progress` | json | Progress tracking information |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | json | Comment body in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Comment author display name |
|
||||
| ↳ `accountId` | string | Comment author account ID |
|
||||
| ↳ `emailAddress` | string | Comment author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the comment |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the comment |
|
||||
| ↳ `created` | string | Comment creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Comment last updated date \(ISO format\) |
|
||||
| ↳ `self` | string | REST API URL for this comment |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Issue Commented
|
||||
|
||||
Trigger workflow when a comment is added to a Jira issue
|
||||
@@ -203,10 +31,6 @@ Trigger workflow when a comment is added to a Jira issue
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
@@ -222,20 +46,19 @@ Trigger workflow when a comment is added to a Jira issue
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Creator email address |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Assignee email address |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
@@ -243,31 +66,22 @@ Trigger workflow when a comment is added to a Jira issue
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Reporter email address |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | json | Comment body in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `body` | string | Comment text/body |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Comment author display name |
|
||||
| ↳ `accountId` | string | Comment author account ID |
|
||||
| ↳ `emailAddress` | string | Comment author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the comment |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the comment |
|
||||
| ↳ `created` | string | Comment creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Comment last updated date \(ISO format\) |
|
||||
| ↳ `self` | string | REST API URL for this comment |
|
||||
|
||||
|
||||
---
|
||||
@@ -289,10 +103,6 @@ Trigger workflow when a new issue is created in Jira
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
@@ -308,20 +118,19 @@ Trigger workflow when a new issue is created in Jira
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Creator email address |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Assignee email address |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
@@ -329,18 +138,13 @@ Trigger workflow when a new issue is created in Jira
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Reporter email address |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira \(only present in issue events\) |
|
||||
|
||||
|
||||
@@ -363,10 +167,6 @@ Trigger workflow when an issue is deleted in Jira
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
@@ -382,20 +182,19 @@ Trigger workflow when an issue is deleted in Jira
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Creator email address |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Assignee email address |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
@@ -403,18 +202,13 @@ Trigger workflow when an issue is deleted in Jira
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Reporter email address |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira \(only present in issue events\) |
|
||||
|
||||
|
||||
@@ -438,10 +232,6 @@ Trigger workflow when an issue is updated in Jira
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
@@ -457,20 +247,19 @@ Trigger workflow when an issue is updated in Jira
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Creator email address |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Assignee email address |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
@@ -478,194 +267,18 @@ Trigger workflow when an issue is updated in Jira
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Reporter email address |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira \(only present in issue events\) |
|
||||
| `changelog` | object | changelog output from the tool |
|
||||
| ↳ `id` | string | Changelog ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Project Created
|
||||
|
||||
Trigger workflow when a project is created in Jira
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(project_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `project` | object | project output from the tool |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `self` | string | REST API URL for this project |
|
||||
| ↳ `projectTypeKey` | string | Project type \(e.g., software, business\) |
|
||||
| ↳ `lead` | object | lead output from the tool |
|
||||
| ↳ `displayName` | string | Project lead display name |
|
||||
| ↳ `accountId` | string | Project lead account ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Sprint Closed
|
||||
|
||||
Trigger workflow when a sprint is closed in Jira
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., sprint_started, sprint_closed\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `sprint` | object | sprint output from the tool |
|
||||
| ↳ `id` | number | Sprint ID |
|
||||
| ↳ `self` | string | REST API URL for this sprint |
|
||||
| ↳ `state` | string | Sprint state \(future, active, closed\) |
|
||||
| ↳ `name` | string | Sprint name |
|
||||
| ↳ `startDate` | string | Sprint start date \(ISO format\) |
|
||||
| ↳ `endDate` | string | Sprint end date \(ISO format\) |
|
||||
| ↳ `completeDate` | string | Sprint completion date \(ISO format\) |
|
||||
| ↳ `originBoardId` | number | Board ID the sprint belongs to |
|
||||
| ↳ `goal` | string | Sprint goal |
|
||||
| ↳ `createdDate` | string | Sprint creation date \(ISO format\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Sprint Created
|
||||
|
||||
Trigger workflow when a sprint is created in Jira
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., sprint_started, sprint_closed\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `sprint` | object | sprint output from the tool |
|
||||
| ↳ `id` | number | Sprint ID |
|
||||
| ↳ `self` | string | REST API URL for this sprint |
|
||||
| ↳ `state` | string | Sprint state \(future, active, closed\) |
|
||||
| ↳ `name` | string | Sprint name |
|
||||
| ↳ `startDate` | string | Sprint start date \(ISO format\) |
|
||||
| ↳ `endDate` | string | Sprint end date \(ISO format\) |
|
||||
| ↳ `completeDate` | string | Sprint completion date \(ISO format\) |
|
||||
| ↳ `originBoardId` | number | Board ID the sprint belongs to |
|
||||
| ↳ `goal` | string | Sprint goal |
|
||||
| ↳ `createdDate` | string | Sprint creation date \(ISO format\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Sprint Started
|
||||
|
||||
Trigger workflow when a sprint is started in Jira
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., sprint_started, sprint_closed\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `sprint` | object | sprint output from the tool |
|
||||
| ↳ `id` | number | Sprint ID |
|
||||
| ↳ `self` | string | REST API URL for this sprint |
|
||||
| ↳ `state` | string | Sprint state \(future, active, closed\) |
|
||||
| ↳ `name` | string | Sprint name |
|
||||
| ↳ `startDate` | string | Sprint start date \(ISO format\) |
|
||||
| ↳ `endDate` | string | Sprint end date \(ISO format\) |
|
||||
| ↳ `completeDate` | string | Sprint completion date \(ISO format\) |
|
||||
| ↳ `originBoardId` | number | Board ID the sprint belongs to |
|
||||
| ↳ `goal` | string | Sprint goal |
|
||||
| ↳ `createdDate` | string | Sprint creation date \(ISO format\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Version Released
|
||||
|
||||
Trigger workflow when a version is released in Jira
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(jira:version_released\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `version` | object | version output from the tool |
|
||||
| ↳ `id` | string | Version ID |
|
||||
| ↳ `name` | string | Version name |
|
||||
| ↳ `self` | string | REST API URL for this version |
|
||||
| ↳ `released` | boolean | Whether the version is released |
|
||||
| ↳ `releaseDate` | string | Release date \(ISO format\) |
|
||||
| ↳ `projectId` | number | Project ID the version belongs to |
|
||||
| ↳ `description` | string | Version description |
|
||||
| ↳ `archived` | boolean | Whether the version is archived |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Webhook (All Events)
|
||||
@@ -724,10 +337,6 @@ Trigger workflow when time is logged on a Jira issue
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
@@ -743,20 +352,19 @@ Trigger workflow when time is logged on a Jira issue
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Creator email address |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Assignee email address |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
@@ -764,213 +372,21 @@ Trigger workflow when time is logged on a Jira issue
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `emailAddress` | string | Reporter email address |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `worklog` | object | worklog output from the tool |
|
||||
| ↳ `id` | string | Worklog entry ID |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Worklog author display name |
|
||||
| ↳ `accountId` | string | Worklog author account ID |
|
||||
| ↳ `emailAddress` | string | Worklog author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the worklog |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the worklog |
|
||||
| ↳ `timeSpent` | string | Time spent \(e.g., "2h 30m"\) |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| ↳ `comment` | string | Worklog comment/description |
|
||||
| ↳ `started` | string | When the work was started \(ISO format\) |
|
||||
| ↳ `created` | string | When the worklog entry was created \(ISO format\) |
|
||||
| ↳ `updated` | string | When the worklog entry was last updated \(ISO format\) |
|
||||
| ↳ `issueId` | string | ID of the issue this worklog belongs to |
|
||||
| ↳ `self` | string | REST API URL for this worklog entry |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Worklog Deleted
|
||||
|
||||
Trigger workflow when a worklog entry is deleted from a Jira issue
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which worklog deletions trigger this workflow using JQL |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `votes` | json | Votes on this issue |
|
||||
| ↳ `labels` | array | Array of labels applied to this issue |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `created` | string | Issue creation date \(ISO format\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `progress` | json | Progress tracking information |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `worklog` | object | worklog output from the tool |
|
||||
| ↳ `id` | string | Worklog entry ID |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Worklog author display name |
|
||||
| ↳ `accountId` | string | Worklog author account ID |
|
||||
| ↳ `emailAddress` | string | Worklog author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the worklog |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the worklog |
|
||||
| ↳ `timeSpent` | string | Time spent \(e.g., "2h 30m"\) |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| ↳ `comment` | string | Worklog comment/description |
|
||||
| ↳ `started` | string | When the work was started \(ISO format\) |
|
||||
| ↳ `created` | string | When the worklog entry was created \(ISO format\) |
|
||||
| ↳ `updated` | string | When the worklog entry was last updated \(ISO format\) |
|
||||
| ↳ `issueId` | string | ID of the issue this worklog belongs to |
|
||||
| ↳ `self` | string | REST API URL for this worklog entry |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Jira Worklog Updated
|
||||
|
||||
Trigger workflow when a worklog entry is updated on a Jira issue
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which worklog updates trigger this workflow using JQL |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, comment_created, worklog_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| ↳ `emailAddress` | string | Email address of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Jira issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `votes` | json | Votes on this issue |
|
||||
| ↳ `labels` | array | Array of labels applied to this issue |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `created` | string | Issue creation date \(ISO format\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `duedate` | string | Due date for the issue |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `summary` | string | Issue summary/title |
|
||||
| ↳ `description` | json | Issue description in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `watches` | json | Watchers information |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `progress` | json | Progress tracking information |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `security` | string | Security level |
|
||||
| ↳ `subtasks` | array | Array of subtask objects |
|
||||
| ↳ `versions` | array | Array of affected versions |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `components` | array | Array of component objects associated with this issue |
|
||||
| ↳ `fixVersions` | array | Array of fix version objects for this issue |
|
||||
| `worklog` | object | worklog output from the tool |
|
||||
| ↳ `id` | string | Worklog entry ID |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Worklog author display name |
|
||||
| ↳ `accountId` | string | Worklog author account ID |
|
||||
| ↳ `emailAddress` | string | Worklog author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the worklog |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the worklog |
|
||||
| ↳ `timeSpent` | string | Time spent \(e.g., "2h 30m"\) |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| ↳ `comment` | string | Worklog comment/description |
|
||||
| ↳ `started` | string | When the work was started \(ISO format\) |
|
||||
| ↳ `created` | string | When the worklog entry was created \(ISO format\) |
|
||||
| ↳ `updated` | string | When the worklog entry was last updated \(ISO format\) |
|
||||
| ↳ `issueId` | string | ID of the issue this worklog belongs to |
|
||||
| ↳ `self` | string | REST API URL for this worklog entry |
|
||||
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
---
|
||||
title: Jsm
|
||||
description: Available Jsm triggers for automating workflows
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="jsm"
|
||||
color="#6B7280"
|
||||
/>
|
||||
|
||||
Jsm provides 5 triggers for automating workflows based on events.
|
||||
|
||||
## Triggers
|
||||
|
||||
### JSM Request Commented
|
||||
|
||||
Trigger workflow when a comment is added to a Jira Service Management request
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which service desk requests trigger this workflow using JQL \(Jira Query Language\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, jira:issue_updated, comment_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., SD-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `summary` | string | Request summary/title |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Current status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Service Request, Incident\) |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `created` | string | Request creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `duedate` | string | Due date for the request |
|
||||
| ↳ `labels` | array | Array of labels applied to this request |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | json | Comment body in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Comment author display name |
|
||||
| ↳ `accountId` | string | Comment author account ID |
|
||||
| ↳ `emailAddress` | string | Comment author email address |
|
||||
| ↳ `updateAuthor` | object | updateAuthor output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who last updated the comment |
|
||||
| ↳ `accountId` | string | Account ID of the user who last updated the comment |
|
||||
| ↳ `created` | string | Comment creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Comment last updated date \(ISO format\) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### JSM Request Created
|
||||
|
||||
Trigger workflow when a new service request is created in Jira Service Management
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which service desk requests trigger this workflow using JQL \(Jira Query Language\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, jira:issue_updated, comment_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., SD-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `summary` | string | Request summary/title |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Current status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Service Request, Incident\) |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `created` | string | Request creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `duedate` | string | Due date for the request |
|
||||
| ↳ `labels` | array | Array of labels applied to this request |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### JSM Request Resolved
|
||||
|
||||
Trigger workflow when a service request is resolved in Jira Service Management
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which service desk requests trigger this workflow using JQL \(Jira Query Language\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, jira:issue_updated, comment_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., SD-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `summary` | string | Request summary/title |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Current status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Service Request, Incident\) |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `created` | string | Request creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `duedate` | string | Due date for the request |
|
||||
| ↳ `labels` | array | Array of labels applied to this request |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira |
|
||||
| `changelog` | object | changelog output from the tool |
|
||||
| ↳ `id` | string | Changelog ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### JSM Request Updated
|
||||
|
||||
Trigger workflow when a service request is updated in Jira Service Management
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which service desk requests trigger this workflow using JQL \(Jira Query Language\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `webhookEvent` | string | The webhook event type \(e.g., jira:issue_created, jira:issue_updated, comment_created\) |
|
||||
| `timestamp` | number | Timestamp of the webhook event |
|
||||
| `user` | object | user output from the tool |
|
||||
| ↳ `displayName` | string | Display name of the user who triggered the event |
|
||||
| ↳ `accountId` | string | Account ID of the user who triggered the event |
|
||||
| `issue` | object | issue output from the tool |
|
||||
| ↳ `id` | string | Jira issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., SD-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `fields` | object | fields output from the tool |
|
||||
| ↳ `summary` | string | Request summary/title |
|
||||
| ↳ `status` | object | status output from the tool |
|
||||
| ↳ `name` | string | Current status name |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `statusCategory` | json | Status category information |
|
||||
| ↳ `priority` | object | priority output from the tool |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `issuetype` | object | issuetype output from the tool |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Service Request, Incident\) |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `project` | object | project output from the tool |
|
||||
| ↳ `key` | string | Project key |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `reporter` | object | reporter output from the tool |
|
||||
| ↳ `displayName` | string | Reporter display name |
|
||||
| ↳ `accountId` | string | Reporter account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `assignee` | object | assignee output from the tool |
|
||||
| ↳ `displayName` | string | Assignee display name |
|
||||
| ↳ `accountId` | string | Assignee account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `creator` | object | creator output from the tool |
|
||||
| ↳ `displayName` | string | Creator display name |
|
||||
| ↳ `accountId` | string | Creator account ID |
|
||||
| ↳ `emailAddress` | string | Email address \(Jira Server only — not available in Jira Cloud webhook payloads\) |
|
||||
| ↳ `created` | string | Request creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Last updated date \(ISO format\) |
|
||||
| ↳ `duedate` | string | Due date for the request |
|
||||
| ↳ `labels` | array | Array of labels applied to this request |
|
||||
| ↳ `resolution` | object | resolution output from the tool |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Done, Fixed\) |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| `issue_event_type_name` | string | Issue event type name from Jira |
|
||||
| `changelog` | object | changelog output from the tool |
|
||||
| ↳ `id` | string | Changelog ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### JSM Webhook (All Events)
|
||||
|
||||
Trigger workflow on any Jira Service Management webhook event
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Jira using HMAC signature |
|
||||
| `jqlFilter` | string | No | Filter which service desk requests trigger this workflow using JQL \(Jira Query Language\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `changelog` | object | changelog output from the tool |
|
||||
| ↳ `id` | string | Changelog ID |
|
||||
| `comment` | object | comment output from the tool |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | json | Comment body in Atlassian Document Format \(ADF\). On Jira Server this may be a plain string. |
|
||||
| ↳ `author` | object | author output from the tool |
|
||||
| ↳ `displayName` | string | Comment author display name |
|
||||
| ↳ `accountId` | string | Comment author account ID |
|
||||
| ↳ `emailAddress` | string | Comment author email address |
|
||||
| ↳ `created` | string | Comment creation date \(ISO format\) |
|
||||
| ↳ `updated` | string | Comment last updated date \(ISO format\) |
|
||||
|
||||
@@ -27,11 +27,9 @@
|
||||
"imap",
|
||||
"intercom",
|
||||
"jira",
|
||||
"jsm",
|
||||
"lemlist",
|
||||
"linear",
|
||||
"microsoft-teams",
|
||||
"monday",
|
||||
"notion",
|
||||
"outlook",
|
||||
"resend",
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
---
|
||||
title: Monday
|
||||
description: Available Monday triggers for automating workflows
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="monday"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
Monday provides 9 triggers for automating workflows based on events.
|
||||
|
||||
## Triggers
|
||||
|
||||
### Monday Column Value Changed
|
||||
|
||||
Trigger workflow when any column value changes on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `columnId` | string | The ID of the changed column |
|
||||
| `columnType` | string | The type of the changed column |
|
||||
| `columnTitle` | string | The title of the changed column |
|
||||
| `value` | json | The new value of the column |
|
||||
| `previousValue` | json | The previous value of the column |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Item Archived
|
||||
|
||||
Trigger workflow when an item is archived on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Item Created
|
||||
|
||||
Trigger workflow when a new item is created on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Item Deleted
|
||||
|
||||
Trigger workflow when an item is deleted on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Item Moved to Group
|
||||
|
||||
Trigger workflow when an item is moved to any group on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `destGroupId` | string | The destination group ID the item was moved to |
|
||||
| `sourceGroupId` | string | The source group ID the item was moved from |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Item Name Changed
|
||||
|
||||
Trigger workflow when an item name changes on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `columnId` | string | The ID of the changed column |
|
||||
| `columnType` | string | The type of the changed column |
|
||||
| `columnTitle` | string | The title of the changed column |
|
||||
| `value` | json | The new value of the column |
|
||||
| `previousValue` | json | The previous value of the column |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Status Changed
|
||||
|
||||
Trigger workflow when a status column value changes on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `columnId` | string | The ID of the changed column |
|
||||
| `columnType` | string | The type of the changed column |
|
||||
| `columnTitle` | string | The title of the changed column |
|
||||
| `value` | json | The new value of the column |
|
||||
| `previousValue` | json | The previous value of the column |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Subitem Created
|
||||
|
||||
Trigger workflow when a subitem is created on a Monday.com board
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `parentItemId` | string | The parent item ID |
|
||||
| `parentItemBoardId` | string | The parent item board ID |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Monday Update Posted
|
||||
|
||||
Trigger workflow when an update or comment is posted on a Monday.com item
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `boardId` | string | The board ID where the event occurred |
|
||||
| `itemId` | string | The item ID \(pulseId\) |
|
||||
| `itemName` | string | The item name \(pulseName\) |
|
||||
| `groupId` | string | The group ID of the item |
|
||||
| `userId` | string | The ID of the user who triggered the event |
|
||||
| `triggerTime` | string | ISO timestamp of when the event occurred |
|
||||
| `triggerUuid` | string | Unique identifier for this event |
|
||||
| `subscriptionId` | string | The webhook subscription ID |
|
||||
| `updateId` | string | The ID of the created update |
|
||||
| `body` | string | The HTML body of the update |
|
||||
| `textBody` | string | The plain text body of the update |
|
||||
|
||||
@@ -25,7 +25,6 @@ Trigger workflow from Slack events like mentions, messages, and reactions
|
||||
| `signingSecret` | string | Yes | The signing secret from your Slack app to validate request authenticity. |
|
||||
| `botToken` | string | No | The bot token from your Slack app. Required for downloading files attached to messages. |
|
||||
| `includeFiles` | boolean | No | Download and include file attachments from messages. Requires a bot token with files:read scope. |
|
||||
| `setupWizard` | modal | No | Walk through manifest creation, app install, and pasting credentials. |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@vercel/og": "^0.6.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"fumadocs-core": "16.6.7",
|
||||
"fumadocs-mdx": "14.2.8",
|
||||
"fumadocs-openapi": "10.3.13",
|
||||
|
||||
|
Before Width: | Height: | Size: 504 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 488 KiB |
|
Before Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 562 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 726 KiB |
|
Before Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 462 KiB |
@@ -1,30 +0,0 @@
|
||||
# Environment variables required by the @sim/realtime (Socket.IO) server.
|
||||
# These MUST match the corresponding values in apps/sim/.env for auth to work.
|
||||
# See apps/realtime/src/env.ts for the full zod schema.
|
||||
|
||||
# Core
|
||||
NODE_ENV=development
|
||||
PORT=3002
|
||||
|
||||
# Database — must point at the same Postgres as the main app
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/simstudio
|
||||
|
||||
# Auth — shared with apps/sim (Better Auth "Shared Database Session" pattern)
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
BETTER_AUTH_SECRET=your_better_auth_secret_min_32_chars
|
||||
|
||||
# Internal RPC — shared with apps/sim
|
||||
INTERNAL_API_SECRET=your_internal_api_secret_min_32_chars
|
||||
|
||||
# Public app URL — used for CORS allow-list and base URL resolution
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
|
||||
# Optional: Redis for cross-pod room management
|
||||
# Leave unset for single-pod / in-memory rooms
|
||||
# REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Optional: extra Socket.IO CORS allow-list (comma-separated)
|
||||
# ALLOWED_ORIGINS=https://embed.example.com,https://admin.example.com
|
||||
|
||||
# Optional: disable auth entirely for trusted private networks
|
||||
# DISABLE_AUTH=true
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "@sim/realtime",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"bun": ">=1.2.13",
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts",
|
||||
"start": "bun src/index.ts",
|
||||
"type-check": "tsc --noEmit",
|
||||
"lint": "biome check --write --unsafe .",
|
||||
"lint:check": "biome check .",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome format .",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sim/audit": "workspace:*",
|
||||
"@sim/auth": "workspace:*",
|
||||
"@sim/db": "workspace:*",
|
||||
"@sim/logger": "workspace:*",
|
||||
"@sim/realtime-protocol": "workspace:*",
|
||||
"@sim/security": "workspace:*",
|
||||
"@sim/utils": "workspace:*",
|
||||
"@sim/workflow-authz": "workspace:*",
|
||||
"@sim/workflow-persistence": "workspace:*",
|
||||
"@sim/workflow-types": "workspace:*",
|
||||
"@socket.io/redis-adapter": "8.3.0",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"postgres": "^3.4.5",
|
||||
"redis": "5.10.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sim/testing": "workspace:*",
|
||||
"@sim/tsconfig": "workspace:*",
|
||||
"@types/node": "24.2.1",
|
||||
"socket.io-client": "4.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"vitest": "^3.0.8"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { createVerifyAuth } from '@sim/auth/verify'
|
||||
import { env } from '@/env'
|
||||
|
||||
export const ANONYMOUS_USER_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
export const ANONYMOUS_USER = {
|
||||
id: ANONYMOUS_USER_ID,
|
||||
name: 'Anonymous',
|
||||
email: 'anonymous@localhost',
|
||||
emailVerified: true,
|
||||
image: null,
|
||||
} as const
|
||||
|
||||
export const auth = createVerifyAuth({
|
||||
secret: env.BETTER_AUTH_SECRET,
|
||||
baseURL: env.BETTER_AUTH_URL,
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const EnvSchema = z.object({
|
||||
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_URL: z.string().url().optional(),
|
||||
BETTER_AUTH_URL: z.string().url(),
|
||||
BETTER_AUTH_SECRET: z.string().min(32),
|
||||
INTERNAL_API_SECRET: z.string().min(32),
|
||||
NEXT_PUBLIC_APP_URL: z.string().url(),
|
||||
ALLOWED_ORIGINS: z.string().optional(),
|
||||
PORT: z.coerce.number().int().positive().default(3002),
|
||||
DISABLE_AUTH: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((value) => value === 'true' || value === '1'),
|
||||
})
|
||||
|
||||
function parseEnv() {
|
||||
const parsed = EnvSchema.safeParse(process.env)
|
||||
if (!parsed.success) {
|
||||
const formatted = parsed.error.format()
|
||||
throw new Error(`Invalid realtime server environment: ${JSON.stringify(formatted, null, 2)}`)
|
||||
}
|
||||
return parsed.data
|
||||
}
|
||||
|
||||
export const env = parseEnv()
|
||||
|
||||
export const isProd = env.NODE_ENV === 'production'
|
||||
export const isDev = env.NODE_ENV === 'development'
|
||||
export const isTest = env.NODE_ENV === 'test'
|
||||
|
||||
let appHostname = ''
|
||||
try {
|
||||
appHostname = new URL(env.NEXT_PUBLIC_APP_URL).hostname
|
||||
} catch {}
|
||||
export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai')
|
||||
|
||||
export const isAuthDisabled = env.DISABLE_AUTH === true && !isHosted
|
||||
|
||||
export function getBaseUrl(): string {
|
||||
return env.NEXT_PUBLIC_APP_URL
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { setupConnectionHandlers } from '@/handlers/connection'
|
||||
import { setupOperationsHandlers } from '@/handlers/operations'
|
||||
import { setupPresenceHandlers } from '@/handlers/presence'
|
||||
import { setupSubblocksHandlers } from '@/handlers/subblocks'
|
||||
import { setupVariablesHandlers } from '@/handlers/variables'
|
||||
import { setupWorkflowHandlers } from '@/handlers/workflow'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
export function setupAllHandlers(socket: AuthenticatedSocket, roomManager: IRoomManager) {
|
||||
setupWorkflowHandlers(socket, roomManager)
|
||||
setupOperationsHandlers(socket, roomManager)
|
||||
setupSubblocksHandlers(socket, roomManager)
|
||||
setupVariablesHandlers(socket, roomManager)
|
||||
setupPresenceHandlers(socket, roomManager)
|
||||
setupConnectionHandlers(socket, roomManager)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export { MemoryRoomManager } from '@/rooms/memory-manager'
|
||||
export { RedisRoomManager } from '@/rooms/redis-manager'
|
||||
export type { IRoomManager, UserPresence, UserSession, WorkflowRoom } from '@/rooms/types'
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "@sim/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import path from 'node:path'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['**/*.test.{ts,tsx}'],
|
||||
exclude: ['**/node_modules/**', '**/dist/**'],
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
pool: 'threads',
|
||||
testTimeout: 10000,
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: '@sim/db',
|
||||
replacement: path.resolve(__dirname, '../../packages/db'),
|
||||
},
|
||||
{
|
||||
find: '@sim/logger',
|
||||
replacement: path.resolve(__dirname, '../../packages/logger/src'),
|
||||
},
|
||||
{ find: '@', replacement: path.resolve(__dirname, 'src') },
|
||||
],
|
||||
},
|
||||
})
|
||||
@@ -1,6 +0,0 @@
|
||||
process.env.DATABASE_URL ??= 'postgres://localhost/test'
|
||||
process.env.NODE_ENV ??= 'test'
|
||||
process.env.BETTER_AUTH_URL ??= 'http://localhost:3000'
|
||||
process.env.BETTER_AUTH_SECRET ??= 'test-better-auth-secret-at-least-32-chars'
|
||||
process.env.INTERNAL_API_SECRET ??= 'test-internal-api-secret-at-least-32-chars'
|
||||
process.env.NEXT_PUBLIC_APP_URL ??= 'http://localhost:3000'
|
||||
@@ -26,13 +26,6 @@ apps/sim/
|
||||
└── triggers/ # Trigger definitions
|
||||
```
|
||||
|
||||
The Socket.IO collaborative-canvas server lives in a separate workspace at
|
||||
`apps/realtime/`. It shares DB + auth with `apps/sim` via the `@sim/*`
|
||||
packages. Do not add imports from `@/lib/webhooks/providers/*`, `@/executor/*`,
|
||||
`@/blocks/*`, or `@/tools/*` to any package consumed by `apps/realtime` —
|
||||
those heavyweight registries stay in this app. `apps/realtime` calls back
|
||||
into this app only over internal HTTP with `INTERNAL_API_SECRET`.
|
||||
|
||||
### Feature Organization
|
||||
|
||||
Features live under `app/workspace/[workspaceId]/`:
|
||||
|
||||
@@ -10,7 +10,6 @@ import { usePostHog } from 'posthog-js/react'
|
||||
import { Input, Label } from '@/components/emcn'
|
||||
import { client, useSession } from '@/lib/auth/auth-client'
|
||||
import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env'
|
||||
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import { captureClientEvent, captureEvent } from '@/lib/posthog/client'
|
||||
@@ -103,14 +102,10 @@ function SignupFormContent({ githubAvailable, googleAvailable, isProduction }: S
|
||||
useEffect(() => {
|
||||
setTurnstileSiteKey(getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'))
|
||||
}, [])
|
||||
const rawRedirectUrl = searchParams.get('redirect') || searchParams.get('callbackUrl') || ''
|
||||
const isValidRedirectUrl = rawRedirectUrl ? validateCallbackUrl(rawRedirectUrl) : false
|
||||
const invalidCallbackRef = useRef(false)
|
||||
if (rawRedirectUrl && !isValidRedirectUrl && !invalidCallbackRef.current) {
|
||||
invalidCallbackRef.current = true
|
||||
logger.warn('Invalid callback URL detected and blocked:', { url: rawRedirectUrl })
|
||||
}
|
||||
const redirectUrl = isValidRedirectUrl ? rawRedirectUrl : ''
|
||||
const redirectUrl = useMemo(
|
||||
() => searchParams.get('redirect') || searchParams.get('callbackUrl') || '',
|
||||
[searchParams]
|
||||
)
|
||||
const isInviteFlow = useMemo(
|
||||
() =>
|
||||
searchParams.get('invite_flow') === 'true' ||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useEffect, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { client, useSession } from '@/lib/auth/auth-client'
|
||||
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
|
||||
|
||||
const logger = createLogger('useVerification')
|
||||
|
||||
@@ -56,11 +55,8 @@ export function useVerification({
|
||||
}
|
||||
|
||||
const storedRedirectUrl = sessionStorage.getItem('inviteRedirectUrl')
|
||||
if (storedRedirectUrl && validateCallbackUrl(storedRedirectUrl)) {
|
||||
if (storedRedirectUrl) {
|
||||
setRedirectUrl(storedRedirectUrl)
|
||||
} else if (storedRedirectUrl) {
|
||||
logger.warn('Ignoring unsafe stored invite redirect URL', { url: storedRedirectUrl })
|
||||
sessionStorage.removeItem('inviteRedirectUrl')
|
||||
}
|
||||
|
||||
const storedIsInviteFlow = sessionStorage.getItem('isInviteFlow')
|
||||
@@ -71,11 +67,7 @@ export function useVerification({
|
||||
|
||||
const redirectParam = searchParams.get('redirectAfter')
|
||||
if (redirectParam) {
|
||||
if (validateCallbackUrl(redirectParam)) {
|
||||
setRedirectUrl(redirectParam)
|
||||
} else {
|
||||
logger.warn('Ignoring unsafe redirectAfter parameter', { url: redirectParam })
|
||||
}
|
||||
setRedirectUrl(redirectParam)
|
||||
}
|
||||
|
||||
const inviteFlowParam = searchParams.get('invite_flow')
|
||||
|
||||
26
apps/sim/app/(landing)/actions/github.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
|
||||
const DEFAULT_STARS = '19.4k'
|
||||
|
||||
const logger = createLogger('GitHubStars')
|
||||
|
||||
export async function getFormattedGitHubStars(): Promise<string> {
|
||||
try {
|
||||
const response = await fetch('/api/stars', {
|
||||
headers: {
|
||||
'Cache-Control': 'max-age=3600', // Cache for 1 hour
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.warn('Failed to fetch GitHub stars from API')
|
||||
return DEFAULT_STARS
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return data.stars || DEFAULT_STARS
|
||||
} catch (error) {
|
||||
logger.warn('Error fetching GitHub stars:', error)
|
||||
return DEFAULT_STARS
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { z } from 'zod'
|
||||
import { NO_EMAIL_HEADER_CONTROL_CHARS_REGEX } from '@/lib/messaging/email/utils'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
|
||||
export const CONTACT_TOPIC_VALUES = [
|
||||
'general',
|
||||
'support',
|
||||
'integration',
|
||||
'feature_request',
|
||||
'sales',
|
||||
'partnership',
|
||||
'billing',
|
||||
'other',
|
||||
] as const
|
||||
|
||||
export const CONTACT_TOPIC_OPTIONS = [
|
||||
{ value: 'general', label: 'General question' },
|
||||
{ value: 'support', label: 'Technical support' },
|
||||
{ value: 'integration', label: 'Integration request' },
|
||||
{ value: 'feature_request', label: 'Feature request' },
|
||||
{ value: 'sales', label: 'Sales & pricing' },
|
||||
{ value: 'partnership', label: 'Partnership' },
|
||||
{ value: 'billing', label: 'Billing' },
|
||||
{ value: 'other', label: 'Other' },
|
||||
] as const
|
||||
|
||||
export const contactRequestSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Name is required')
|
||||
.max(120, 'Name must be 120 characters or less')
|
||||
.regex(NO_EMAIL_HEADER_CONTROL_CHARS_REGEX, 'Invalid characters'),
|
||||
email: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Email is required')
|
||||
.max(320)
|
||||
.transform((value) => value.toLowerCase())
|
||||
.refine((value) => quickValidateEmail(value).isValid, 'Enter a valid email'),
|
||||
company: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(120, 'Company must be 120 characters or less')
|
||||
.optional()
|
||||
.transform((value) => (value && value.length > 0 ? value : undefined)),
|
||||
topic: z.enum(CONTACT_TOPIC_VALUES, {
|
||||
errorMap: () => ({ message: 'Please select a topic' }),
|
||||
}),
|
||||
subject: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Subject is required')
|
||||
.max(200, 'Subject must be 200 characters or less')
|
||||
.regex(NO_EMAIL_HEADER_CONTROL_CHARS_REGEX, 'Invalid characters'),
|
||||
message: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Message is required')
|
||||
.max(5000, 'Message must be 5,000 characters or less'),
|
||||
})
|
||||
|
||||
export type ContactRequestPayload = z.infer<typeof contactRequestSchema>
|
||||
|
||||
export function getContactTopicLabel(value: ContactRequestPayload['topic']): string {
|
||||
return CONTACT_TOPIC_OPTIONS.find((option) => option.value === value)?.label ?? value
|
||||
}
|
||||
|
||||
export type HelpEmailType = 'bug' | 'feedback' | 'feature_request' | 'other'
|
||||
|
||||
export function mapContactTopicToHelpType(topic: ContactRequestPayload['topic']): HelpEmailType {
|
||||
switch (topic) {
|
||||
case 'feature_request':
|
||||
return 'feature_request'
|
||||
case 'support':
|
||||
return 'bug'
|
||||
case 'integration':
|
||||
return 'feedback'
|
||||
default:
|
||||
return 'other'
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import Link from 'next/link'
|
||||
import { Combobox, type ComboboxOption, Input, Textarea } from '@/components/emcn'
|
||||
import { Check } from '@/components/emcn/icons'
|
||||
import { getEnv } from '@/lib/core/config/env'
|
||||
import { captureClientEvent } from '@/lib/posthog/client'
|
||||
import {
|
||||
CONTACT_TOPIC_OPTIONS,
|
||||
type ContactRequestPayload,
|
||||
contactRequestSchema,
|
||||
} from '@/app/(landing)/components/contact/consts'
|
||||
import { LandingField } from '@/app/(landing)/components/forms/landing-field'
|
||||
|
||||
type ContactField = keyof ContactRequestPayload
|
||||
type ContactErrors = Partial<Record<ContactField, string>>
|
||||
|
||||
interface ContactFormState {
|
||||
name: string
|
||||
email: string
|
||||
company: string
|
||||
topic: ContactRequestPayload['topic'] | ''
|
||||
subject: string
|
||||
message: string
|
||||
}
|
||||
|
||||
const INITIAL_FORM_STATE: ContactFormState = {
|
||||
name: '',
|
||||
email: '',
|
||||
company: '',
|
||||
topic: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
}
|
||||
|
||||
const LANDING_INPUT =
|
||||
'h-[40px] rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg-surface)] px-3 font-[430] font-season text-[14px] text-[var(--landing-text)] outline-none transition-colors placeholder:text-[var(--landing-text-muted)] focus:border-[var(--landing-border-strong)]'
|
||||
|
||||
const LANDING_TEXTAREA =
|
||||
'min-h-[140px] rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg-surface)] px-3 py-2.5 font-[430] font-season text-[14px] text-[var(--landing-text)] outline-none transition-colors placeholder:text-[var(--landing-text-muted)] focus:border-[var(--landing-border-strong)]'
|
||||
|
||||
const LANDING_COMBOBOX =
|
||||
'h-[40px] rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg-surface)] px-3 font-[430] font-season text-[14px] text-[var(--landing-text)] hover:bg-[var(--landing-bg-surface)] focus-within:border-[var(--landing-border-strong)]'
|
||||
|
||||
const LANDING_SUBMIT =
|
||||
'flex h-[40px] w-full items-center justify-center rounded-[5px] border border-[var(--landing-text-subtle)] bg-[var(--landing-text-subtle)] font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:border-[var(--landing-bg-hover)] hover:bg-[var(--landing-bg-hover)] disabled:cursor-not-allowed disabled:opacity-60'
|
||||
|
||||
const LANDING_LABEL =
|
||||
'font-[500] font-season text-[13px] text-[var(--landing-text)] tracking-[0.02em]'
|
||||
|
||||
interface SubmitContactRequestInput extends ContactRequestPayload {
|
||||
website: string
|
||||
captchaToken?: string
|
||||
captchaUnavailable?: boolean
|
||||
}
|
||||
|
||||
async function submitContactRequest(payload: SubmitContactRequestInput) {
|
||||
const response = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const result = (await response.json().catch(() => null)) as {
|
||||
error?: string
|
||||
message?: string
|
||||
} | null
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result?.error || 'Failed to send message')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function ContactForm() {
|
||||
const turnstileRef = useRef<TurnstileInstance>(null)
|
||||
|
||||
const contactMutation = useMutation({
|
||||
mutationFn: submitContactRequest,
|
||||
onSuccess: (_data, variables) => {
|
||||
captureClientEvent('landing_contact_submitted', { topic: variables.topic })
|
||||
setForm(INITIAL_FORM_STATE)
|
||||
setErrors({})
|
||||
setSubmitSuccess(true)
|
||||
},
|
||||
onError: () => {
|
||||
turnstileRef.current?.reset()
|
||||
},
|
||||
})
|
||||
|
||||
const [form, setForm] = useState<ContactFormState>(INITIAL_FORM_STATE)
|
||||
const [errors, setErrors] = useState<ContactErrors>({})
|
||||
const [submitSuccess, setSubmitSuccess] = useState(false)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [website, setWebsite] = useState('')
|
||||
const [widgetReady, setWidgetReady] = useState(false)
|
||||
const [turnstileSiteKey, setTurnstileSiteKey] = useState<string | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
setTurnstileSiteKey(getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'))
|
||||
}, [])
|
||||
|
||||
function updateField<TField extends keyof ContactFormState>(
|
||||
field: TField,
|
||||
value: ContactFormState[TField]
|
||||
) {
|
||||
setForm((prev) => ({ ...prev, [field]: value }))
|
||||
setErrors((prev) => {
|
||||
if (!prev[field as ContactField]) {
|
||||
return prev
|
||||
}
|
||||
const nextErrors = { ...prev }
|
||||
delete nextErrors[field as ContactField]
|
||||
return nextErrors
|
||||
})
|
||||
if (contactMutation.isError) {
|
||||
contactMutation.reset()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
if (contactMutation.isPending || isSubmitting) return
|
||||
setIsSubmitting(true)
|
||||
|
||||
const parsed = contactRequestSchema.safeParse({
|
||||
...form,
|
||||
company: form.company || undefined,
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
const fieldErrors = parsed.error.flatten().fieldErrors
|
||||
setErrors({
|
||||
name: fieldErrors.name?.[0],
|
||||
email: fieldErrors.email?.[0],
|
||||
company: fieldErrors.company?.[0],
|
||||
topic: fieldErrors.topic?.[0],
|
||||
subject: fieldErrors.subject?.[0],
|
||||
message: fieldErrors.message?.[0],
|
||||
})
|
||||
setIsSubmitting(false)
|
||||
return
|
||||
}
|
||||
|
||||
let captchaToken: string | undefined
|
||||
let captchaUnavailable: boolean | undefined
|
||||
const widget = turnstileRef.current
|
||||
|
||||
if (turnstileSiteKey) {
|
||||
if (widgetReady && widget) {
|
||||
try {
|
||||
widget.reset()
|
||||
widget.execute()
|
||||
captchaToken = await widget.getResponsePromise(30_000)
|
||||
} catch {
|
||||
captchaUnavailable = true
|
||||
}
|
||||
} else {
|
||||
captchaUnavailable = true
|
||||
}
|
||||
}
|
||||
|
||||
contactMutation.mutate({ ...parsed.data, website, captchaToken, captchaUnavailable })
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const isBusy = contactMutation.isPending || isSubmitting
|
||||
|
||||
const submitError = contactMutation.isError
|
||||
? toError(contactMutation.error).message || 'Failed to send message. Please try again.'
|
||||
: null
|
||||
|
||||
if (submitSuccess) {
|
||||
return (
|
||||
<div className='flex flex-col items-center px-8 py-16 text-center'>
|
||||
<div className='flex h-16 w-16 items-center justify-center rounded-full border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg-surface)] text-[var(--landing-text)]'>
|
||||
<Check className='h-8 w-8' />
|
||||
</div>
|
||||
<h2 className='mt-6 font-[430] font-season text-[24px] text-[var(--landing-text)] leading-[1.2] tracking-[-0.02em]'>
|
||||
Message received
|
||||
</h2>
|
||||
<p className='mt-3 max-w-sm font-season text-[14px] text-[var(--landing-text-body)] leading-[1.6]'>
|
||||
Thanks for reaching out. We've sent a confirmation to your inbox and will get back to you
|
||||
shortly.
|
||||
</p>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => setSubmitSuccess(false)}
|
||||
className='mt-6 font-season text-[13px] text-[var(--landing-text)] underline underline-offset-2 transition-opacity hover:opacity-80'
|
||||
>
|
||||
Send another message
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className='relative flex flex-col gap-5'>
|
||||
{/* Honeypot */}
|
||||
<div
|
||||
aria-hidden='true'
|
||||
className='pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden opacity-0'
|
||||
>
|
||||
<label htmlFor='contact-website'>Website</label>
|
||||
<input
|
||||
id='contact-website'
|
||||
name='website'
|
||||
type='text'
|
||||
tabIndex={-1}
|
||||
autoComplete='off'
|
||||
value={website}
|
||||
onChange={(event) => setWebsite(event.target.value)}
|
||||
data-lpignore='true'
|
||||
data-1p-ignore='true'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-5 sm:grid-cols-2'>
|
||||
<LandingField
|
||||
htmlFor='contact-name'
|
||||
label='Name'
|
||||
error={errors.name}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Input
|
||||
id='contact-name'
|
||||
value={form.name}
|
||||
onChange={(event) => updateField('name', event.target.value)}
|
||||
placeholder='Your name'
|
||||
className={LANDING_INPUT}
|
||||
/>
|
||||
</LandingField>
|
||||
<LandingField
|
||||
htmlFor='contact-email'
|
||||
label='Email'
|
||||
error={errors.email}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Input
|
||||
id='contact-email'
|
||||
type='email'
|
||||
value={form.email}
|
||||
onChange={(event) => updateField('email', event.target.value)}
|
||||
placeholder='you@company.com'
|
||||
className={LANDING_INPUT}
|
||||
/>
|
||||
</LandingField>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-5 sm:grid-cols-2'>
|
||||
<LandingField
|
||||
htmlFor='contact-company'
|
||||
label='Company'
|
||||
optional
|
||||
error={errors.company}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Input
|
||||
id='contact-company'
|
||||
value={form.company}
|
||||
onChange={(event) => updateField('company', event.target.value)}
|
||||
placeholder='Company name'
|
||||
className={LANDING_INPUT}
|
||||
/>
|
||||
</LandingField>
|
||||
<LandingField
|
||||
htmlFor='contact-topic'
|
||||
label='Topic'
|
||||
error={errors.topic}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Combobox
|
||||
options={CONTACT_TOPIC_OPTIONS as unknown as ComboboxOption[]}
|
||||
value={form.topic}
|
||||
selectedValue={form.topic}
|
||||
onChange={(value) => updateField('topic', value as ContactRequestPayload['topic'])}
|
||||
placeholder='Select a topic'
|
||||
editable={false}
|
||||
filterOptions={false}
|
||||
className={LANDING_COMBOBOX}
|
||||
/>
|
||||
</LandingField>
|
||||
</div>
|
||||
|
||||
<LandingField
|
||||
htmlFor='contact-subject'
|
||||
label='Subject'
|
||||
error={errors.subject}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Input
|
||||
id='contact-subject'
|
||||
value={form.subject}
|
||||
onChange={(event) => updateField('subject', event.target.value)}
|
||||
placeholder='How can we help?'
|
||||
className={LANDING_INPUT}
|
||||
/>
|
||||
</LandingField>
|
||||
|
||||
<LandingField
|
||||
htmlFor='contact-message'
|
||||
label='Message'
|
||||
error={errors.message}
|
||||
labelClassName={LANDING_LABEL}
|
||||
>
|
||||
<Textarea
|
||||
id='contact-message'
|
||||
value={form.message}
|
||||
onChange={(event) => updateField('message', event.target.value)}
|
||||
placeholder='Share details so we can help as quickly as possible'
|
||||
className={LANDING_TEXTAREA}
|
||||
/>
|
||||
</LandingField>
|
||||
|
||||
{turnstileSiteKey ? (
|
||||
<Turnstile
|
||||
ref={turnstileRef}
|
||||
siteKey={turnstileSiteKey}
|
||||
options={{ execution: 'execute', appearance: 'execute', size: 'invisible' }}
|
||||
onWidgetLoad={() => setWidgetReady(true)}
|
||||
onExpire={() => setWidgetReady(false)}
|
||||
onError={() => setWidgetReady(false)}
|
||||
onUnsupported={() => setWidgetReady(false)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{submitError ? (
|
||||
<p role='alert' className='font-season text-[13px] text-[var(--text-error)]'>
|
||||
{submitError}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<button type='submit' disabled={isBusy} className={LANDING_SUBMIT}>
|
||||
{isBusy ? 'Sending...' : 'Send message'}
|
||||
</button>
|
||||
|
||||
<p className='text-center font-season text-[12px] text-[var(--landing-text-muted)] leading-[1.6]'>
|
||||
By submitting, you agree to our{' '}
|
||||
<Link
|
||||
href='/privacy'
|
||||
className='text-[var(--landing-text)] underline underline-offset-2 transition-opacity hover:opacity-80'
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { useCallback, useState } from 'react'
|
||||
import {
|
||||
Combobox,
|
||||
Input,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
type DemoRequestPayload,
|
||||
demoRequestSchema,
|
||||
} from '@/app/(landing)/components/demo-request/consts'
|
||||
import { LandingField } from '@/app/(landing)/components/forms/landing-field'
|
||||
|
||||
interface DemoRequestModalProps {
|
||||
children: React.ReactNode
|
||||
@@ -51,104 +49,136 @@ const INITIAL_FORM_STATE: DemoRequestFormState = {
|
||||
details: '',
|
||||
}
|
||||
|
||||
interface LandingFieldProps {
|
||||
label: string
|
||||
htmlFor: string
|
||||
optional?: boolean
|
||||
error?: string
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function LandingField({ label, htmlFor, optional, error, children }: LandingFieldProps) {
|
||||
return (
|
||||
<div className='flex flex-col gap-1.5'>
|
||||
<label
|
||||
htmlFor={htmlFor}
|
||||
className='font-[430] font-season text-[13px] text-[var(--text-secondary)] tracking-[0.02em]'
|
||||
>
|
||||
{label}
|
||||
{optional ? <span className='ml-1 text-[var(--text-muted)]'>(optional)</span> : null}
|
||||
</label>
|
||||
{children}
|
||||
{error ? <p className='text-[12px] text-[var(--text-error)]'>{error}</p> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LANDING_INPUT =
|
||||
'h-[32px] rounded-[5px] border border-[var(--border-1)] bg-[var(--surface-5)] px-2.5 font-[430] font-season text-[13.5px] text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none'
|
||||
|
||||
async function submitDemoRequest(payload: DemoRequestPayload) {
|
||||
const response = await fetch('/api/demo-requests', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const result = (await response.json().catch(() => null)) as {
|
||||
error?: string
|
||||
message?: string
|
||||
} | null
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result?.error || 'Failed to submit demo request')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function DemoRequestModal({ children, theme = 'dark' }: DemoRequestModalProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [form, setForm] = useState<DemoRequestFormState>(INITIAL_FORM_STATE)
|
||||
const [errors, setErrors] = useState<DemoRequestErrors>({})
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [submitError, setSubmitError] = useState<string | null>(null)
|
||||
const [submitSuccess, setSubmitSuccess] = useState(false)
|
||||
|
||||
const demoMutation = useMutation({
|
||||
mutationFn: submitDemoRequest,
|
||||
onSuccess: (_data, variables) => {
|
||||
captureClientEvent('landing_demo_request_submitted', {
|
||||
company_size: variables.companySize,
|
||||
})
|
||||
setSubmitSuccess(true)
|
||||
},
|
||||
})
|
||||
|
||||
function resetForm() {
|
||||
const resetForm = useCallback(() => {
|
||||
setForm(INITIAL_FORM_STATE)
|
||||
setErrors({})
|
||||
setIsSubmitting(false)
|
||||
setSubmitError(null)
|
||||
setSubmitSuccess(false)
|
||||
demoMutation.reset()
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleOpenChange(nextOpen: boolean) {
|
||||
setOpen(nextOpen)
|
||||
resetForm()
|
||||
}
|
||||
const handleOpenChange = useCallback(
|
||||
(nextOpen: boolean) => {
|
||||
setOpen(nextOpen)
|
||||
resetForm()
|
||||
},
|
||||
[resetForm]
|
||||
)
|
||||
|
||||
function updateField<TField extends keyof DemoRequestFormState>(
|
||||
field: TField,
|
||||
value: DemoRequestFormState[TField]
|
||||
) {
|
||||
setForm((prev) => ({ ...prev, [field]: value }))
|
||||
setErrors((prev) => {
|
||||
if (!prev[field]) {
|
||||
return prev
|
||||
}
|
||||
const nextErrors = { ...prev }
|
||||
delete nextErrors[field]
|
||||
return nextErrors
|
||||
})
|
||||
if (demoMutation.isError) {
|
||||
demoMutation.reset()
|
||||
}
|
||||
}
|
||||
const updateField = useCallback(
|
||||
<TField extends keyof DemoRequestFormState>(
|
||||
field: TField,
|
||||
value: DemoRequestFormState[TField]
|
||||
) => {
|
||||
setForm((prev) => ({ ...prev, [field]: value }))
|
||||
setErrors((prev) => {
|
||||
if (!prev[field]) {
|
||||
return prev
|
||||
}
|
||||
|
||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
if (demoMutation.isPending) return
|
||||
|
||||
const parsed = demoRequestSchema.safeParse({
|
||||
...form,
|
||||
phoneNumber: form.phoneNumber || undefined,
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
const fieldErrors = parsed.error.flatten().fieldErrors
|
||||
setErrors({
|
||||
firstName: fieldErrors.firstName?.[0],
|
||||
lastName: fieldErrors.lastName?.[0],
|
||||
companyEmail: fieldErrors.companyEmail?.[0],
|
||||
phoneNumber: fieldErrors.phoneNumber?.[0],
|
||||
companySize: fieldErrors.companySize?.[0],
|
||||
details: fieldErrors.details?.[0],
|
||||
const nextErrors = { ...prev }
|
||||
delete nextErrors[field]
|
||||
return nextErrors
|
||||
})
|
||||
return
|
||||
}
|
||||
setSubmitError(null)
|
||||
setSubmitSuccess(false)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
demoMutation.mutate(parsed.data)
|
||||
}
|
||||
const handleSubmit = useCallback(
|
||||
async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault()
|
||||
setSubmitError(null)
|
||||
setSubmitSuccess(false)
|
||||
|
||||
const submitError = demoMutation.isError
|
||||
? demoMutation.error instanceof Error
|
||||
? demoMutation.error.message
|
||||
: 'Failed to submit demo request. Please try again.'
|
||||
: null
|
||||
const parsed = demoRequestSchema.safeParse({
|
||||
...form,
|
||||
phoneNumber: form.phoneNumber || undefined,
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
const fieldErrors = parsed.error.flatten().fieldErrors
|
||||
setErrors({
|
||||
firstName: fieldErrors.firstName?.[0],
|
||||
lastName: fieldErrors.lastName?.[0],
|
||||
companyEmail: fieldErrors.companyEmail?.[0],
|
||||
phoneNumber: fieldErrors.phoneNumber?.[0],
|
||||
companySize: fieldErrors.companySize?.[0],
|
||||
details: fieldErrors.details?.[0],
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
setIsSubmitting(true)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/demo-requests', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(parsed.data),
|
||||
})
|
||||
|
||||
const result = (await response.json().catch(() => null)) as {
|
||||
error?: string
|
||||
message?: string
|
||||
} | null
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result?.error || 'Failed to submit demo request')
|
||||
}
|
||||
|
||||
setSubmitSuccess(true)
|
||||
captureClientEvent('landing_demo_request_submitted', {
|
||||
company_size: parsed.data.companySize,
|
||||
})
|
||||
} catch (error) {
|
||||
setSubmitError(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to submit demo request. Please try again.'
|
||||
)
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
},
|
||||
[form, resetForm]
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal open={open} onOpenChange={handleOpenChange}>
|
||||
@@ -254,16 +284,14 @@ export function DemoRequestModal({ children, theme = 'dark' }: DemoRequestModalP
|
||||
|
||||
<ModalFooter className='flex-col items-stretch gap-3 border-t-0 bg-transparent pt-0'>
|
||||
{submitError && (
|
||||
<p role='alert' className='font-season text-[13px] text-[var(--text-error)]'>
|
||||
{submitError}
|
||||
</p>
|
||||
<p className='font-season text-[13px] text-[var(--text-error)]'>{submitError}</p>
|
||||
)}
|
||||
<button
|
||||
type='submit'
|
||||
disabled={demoMutation.isPending}
|
||||
disabled={isSubmitting}
|
||||
className='flex h-[32px] w-full items-center justify-center rounded-[5px] bg-[var(--text-primary)] font-[430] font-season text-[13.5px] text-[var(--bg)] transition-colors hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50'
|
||||
>
|
||||
{demoMutation.isPending ? 'Submitting...' : 'Submit'}
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
|
||||
@@ -31,7 +31,6 @@ const RESOURCES_LINKS: FooterItem[] = [
|
||||
{ label: 'Partners', href: '/partners' },
|
||||
{ label: 'Careers', href: 'https://jobs.ashbyhq.com/sim', external: true, externalArrow: true },
|
||||
{ label: 'Changelog', href: '/changelog' },
|
||||
{ label: 'Contact', href: '/contact' },
|
||||
]
|
||||
|
||||
const BLOCK_LINKS: FooterItem[] = [
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { cloneElement, isValidElement } from 'react'
|
||||
|
||||
interface LandingFieldProps {
|
||||
label: string
|
||||
htmlFor: string
|
||||
optional?: boolean
|
||||
error?: string
|
||||
children: React.ReactNode
|
||||
/** Replaces the default label className. */
|
||||
labelClassName?: string
|
||||
}
|
||||
|
||||
const DEFAULT_LABEL_CLASSNAME =
|
||||
'font-[430] font-season text-[13px] text-[var(--text-secondary)] tracking-[0.02em]'
|
||||
|
||||
export function LandingField({
|
||||
label,
|
||||
htmlFor,
|
||||
optional,
|
||||
error,
|
||||
children,
|
||||
labelClassName,
|
||||
}: LandingFieldProps) {
|
||||
const errorId = error ? `${htmlFor}-error` : undefined
|
||||
const describedChild =
|
||||
errorId && isValidElement<{ 'aria-describedby'?: string; 'aria-invalid'?: boolean }>(children)
|
||||
? cloneElement(children, { 'aria-describedby': errorId, 'aria-invalid': true })
|
||||
: children
|
||||
return (
|
||||
<div className='flex flex-col gap-1.5'>
|
||||
<div className='flex min-h-[18px] items-baseline justify-between gap-3'>
|
||||
<label htmlFor={htmlFor} className={labelClassName ?? DEFAULT_LABEL_CLASSNAME}>
|
||||
{label}
|
||||
{optional ? <span className='ml-1 text-[var(--text-muted)]'>(optional)</span> : null}
|
||||
</label>
|
||||
{error ? (
|
||||
<span
|
||||
id={errorId}
|
||||
role='alert'
|
||||
className='truncate font-season text-[12px] text-[var(--text-error)]'
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{describedChild}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { GithubOutlineIcon } from '@/components/icons'
|
||||
import { useGitHubStars } from '@/hooks/queries/github-stars'
|
||||
import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'
|
||||
|
||||
const logger = createLogger('github-stars')
|
||||
|
||||
const INITIAL_STARS = '27.7k'
|
||||
|
||||
/**
|
||||
* Client component that displays GitHub stars count.
|
||||
@@ -10,7 +16,15 @@ import { useGitHubStars } from '@/hooks/queries/github-stars'
|
||||
* a Server Component for optimal SEO/GEO crawlability.
|
||||
*/
|
||||
export function GitHubStars() {
|
||||
const { data: stars } = useGitHubStars()
|
||||
const [stars, setStars] = useState(INITIAL_STARS)
|
||||
|
||||
useEffect(() => {
|
||||
getFormattedGitHubStars()
|
||||
.then(setStars)
|
||||
.catch((error) => {
|
||||
logger.warn('Failed to fetch GitHub stars', error)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<a
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { getNavBlogPosts } from '@/lib/blog/registry'
|
||||
import AuthBackground from '@/app/(auth)/components/auth-background'
|
||||
import { AUTH_PRIMARY_CTA_BASE } from '@/app/(auth)/components/auth-button-classes'
|
||||
import Navbar from '@/app/(landing)/components/navbar/navbar'
|
||||
|
||||
/**
|
||||
* Shared 404 view used by every `not-found.tsx` under the landing surface.
|
||||
*
|
||||
* Rendered outside the route-group `(shell)` layout so it owns the full
|
||||
* viewport (Navbar + AuthBackground decoration, no Footer), matching the
|
||||
* root `/` 404 treatment.
|
||||
*/
|
||||
export default async function NotFoundView() {
|
||||
const blogPosts = await getNavBlogPosts()
|
||||
return (
|
||||
<AuthBackground className='dark font-[430] font-season'>
|
||||
<main className='relative flex min-h-full flex-col text-[var(--landing-text)]'>
|
||||
<header className='shrink-0 bg-[var(--landing-bg)]'>
|
||||
<Navbar blogPosts={blogPosts} />
|
||||
</header>
|
||||
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
|
||||
<div className='flex flex-col items-center gap-3'>
|
||||
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
|
||||
Page not found
|
||||
</h1>
|
||||
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
|
||||
The page you're looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
<div className='mt-3 flex items-center gap-2'>
|
||||
<Link href='/' className={AUTH_PRIMARY_CTA_BASE}>
|
||||
Return to Home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</AuthBackground>
|
||||
)
|
||||
}
|
||||
@@ -39,7 +39,6 @@ const PRICING_TIERS: PricingTier[] = [
|
||||
'1,000 credits (trial)',
|
||||
'5GB file storage',
|
||||
'3 tables · 1,000 rows each',
|
||||
'1 personal workspace',
|
||||
'5 min execution limit',
|
||||
'7-day log retention',
|
||||
'CLI/SDK/MCP Access',
|
||||
@@ -57,7 +56,6 @@ const PRICING_TIERS: PricingTier[] = [
|
||||
'6,000 credits/mo · +50/day',
|
||||
'50GB file storage',
|
||||
'25 tables · 5,000 rows each',
|
||||
'Up to 3 personal workspaces',
|
||||
'50 min execution · 150 runs/min',
|
||||
'Unlimited log retention',
|
||||
'CLI/SDK/MCP Access',
|
||||
@@ -75,7 +73,6 @@ const PRICING_TIERS: PricingTier[] = [
|
||||
'25,000 credits/mo · +200/day',
|
||||
'500GB file storage',
|
||||
'25 tables · 5,000 rows each',
|
||||
'Up to 10 personal workspaces',
|
||||
'50 min execution · 300 runs/min',
|
||||
'Unlimited log retention',
|
||||
'CLI/SDK/MCP Access',
|
||||
@@ -92,7 +89,6 @@ const PRICING_TIERS: PricingTier[] = [
|
||||
'Custom credits & infra limits',
|
||||
'Custom file storage',
|
||||
'10,000 tables · 1M rows each',
|
||||
'Unlimited shared workspaces',
|
||||
'Custom execution limits',
|
||||
'Unlimited log retention',
|
||||
'SSO & SCIM · SOC2',
|
||||
@@ -268,12 +264,10 @@ export default function Pricing() {
|
||||
Pricing
|
||||
</h2>
|
||||
<p className='sr-only'>
|
||||
Sim pricing: Community plan is free with 1,000 credits, 5GB storage, and 1 personal
|
||||
workspace. Pro plan is $25 per month with 6,000 credits, 50GB storage, and up to 3
|
||||
personal workspaces. Max plan is $100 per month with 25,000 credits, 500GB storage, and
|
||||
up to 10 personal workspaces. Enterprise pricing is custom with unlimited shared
|
||||
workspaces, SSO, SCIM, SOC2 compliance, self-hosting, and dedicated support. All plans
|
||||
include CLI, SDK, and MCP access.
|
||||
Sim pricing: Community plan is free with 1,000 credits and 5GB storage. Pro plan is $25
|
||||
per month with 6,000 credits and 50GB storage. Max plan is $100 per month with 25,000
|
||||
credits and 500GB storage. Enterprise pricing is custom with SSO, SCIM, SOC2 compliance,
|
||||
self-hosting, and dedicated support. All plans include CLI, SDK, and MCP access.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { getNavBlogPosts } from '@/lib/blog/registry'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import { ContactForm } from '@/app/(landing)/components/contact/contact-form'
|
||||
import Footer from '@/app/(landing)/components/footer/footer'
|
||||
import Navbar from '@/app/(landing)/components/navbar/navbar'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Contact Us',
|
||||
description:
|
||||
'Get in touch with Sim. Ask a general question, request an integration, or get help.',
|
||||
metadataBase: new URL(SITE_URL),
|
||||
alternates: { canonical: '/contact' },
|
||||
openGraph: {
|
||||
title: 'Contact Us | Sim',
|
||||
description: 'Get in touch with the Sim team for questions, integrations, and support.',
|
||||
type: 'website',
|
||||
},
|
||||
}
|
||||
|
||||
export default async function ContactPage() {
|
||||
const blogPosts = await getNavBlogPosts()
|
||||
|
||||
return (
|
||||
<main className='min-h-screen bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
|
||||
<header>
|
||||
<Navbar blogPosts={blogPosts} />
|
||||
</header>
|
||||
|
||||
<div className='mx-auto max-w-[640px] px-6 pt-[72px] pb-24 sm:px-12'>
|
||||
<span className='mb-4 block font-martian-mono text-[11px] text-[var(--landing-text-muted)] uppercase tracking-[0.12em]'>
|
||||
Contact us
|
||||
</span>
|
||||
<h1 className='mb-5 text-balance font-[500] text-4xl text-[var(--landing-text)] leading-[1.05] tracking-[-0.02em] md:text-5xl'>
|
||||
We're here to help
|
||||
</h1>
|
||||
<p className='text-pretty text-[var(--landing-text-muted)] text-base leading-[1.7]'>
|
||||
Got a general question, integration request, or need help? Send us a message and our team
|
||||
will get back to you.
|
||||
</p>
|
||||
|
||||
<div className='dark mt-14'>
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isHosted && <Footer hideCTA />}
|
||||
</main>
|
||||
)
|
||||
}
|
||||