docyrus-api-dev

Develop applications using the Docyrus API with @docyrus/api-client and @docyrus/signin libraries. Use when building apps that authenticate with Docyrus OAuth2…

INSTALLATION
npx skills add https://github.com/docyrus/agent-skills --skill docyrus-api-dev
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Docyrus API Developer

Integrate with the Docyrus API using @docyrus/api-client (REST client) and @docyrus/signin (React auth provider). Authenticate via OAuth2 PKCE, query data sources with powerful filtering/aggregation, and consume REST endpoints.

Authentication Quick Start

React Apps — Use @docyrus/signin

import { DocyrusAuthProvider, useDocyrusAuth, useDocyrusClient, SignInButton } from '@docyrus/signin'

// 1. Wrap root

<DocyrusAuthProvider

apiUrl={import.meta.env.VITE_API_BASE_URL}

clientId={import.meta.env.VITE_OAUTH2_CLIENT_ID}

redirectUri={import.meta.env.VITE_OAUTH2_REDIRECT_URI}

scopes={['offline_access', 'Read.All', 'DS.ReadWrite.All', 'Users.Read']}

callbackPath="/auth/callback"

// 2. Use hooks

function App() {

const { status, signOut } = useDocyrusAuth()

const client = useDocyrusClient() // RestApiClient | null

if (status === 'loading') return

if (status === 'unauthenticated') return

// client is ready — make API calls

const user = await client!.get('/v1/users/me')

}

### Non-React / Server — Use OAuth2Client Directly

import { RestApiClient, OAuth2Client, OAuth2TokenManagerAdapter, BrowserOAuth2TokenStorage } from '@docyrus/api-client'

const tokenStorage = new BrowserOAuth2TokenStorage(localStorage)

const oauth2 = new OAuth2Client({

baseURL: 'https://api.docyrus.com',

clientId: 'your-client-id',

redirectUri: 'http://localhost:3000/callback',

usePKCE: true,

tokenStorage,

})

// Auth Code flow

const { url } = await oauth2.getAuthorizationUrl({ scope: 'openid offline_access Users.Read' })

window.location.href = url

// After redirect:

const tokens = await oauth2.handleCallback(window.location.href)

// Create API client with auto-refresh

const client = new RestApiClient({

baseURL: 'https://api.docyrus.com',

tokenManager: new OAuth2TokenManagerAdapter(tokenStorage, async () => {

return (await oauth2.refreshAccessToken()).accessToken

}),

})


## API Endpoints

### Data Source Items (Dynamic per tenant)

GET /v1/apps/{appSlug}/data-sources/{slug}/items — List with query payload

GET /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Get one

POST /v1/apps/{appSlug}/data-sources/{slug}/items — Create

PATCH /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Update

DELETE /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Delete one

DELETE /v1/apps/{appSlug}/data-sources/{slug}/items — Delete many (body: { recordIds })


Endpoints exist only if the data source is defined in the tenant. Check the tenant's OpenAPI spec at `GET /v1/api/openapi.json`.

### System Endpoints (Always Available)

GET /v1/users — List users

POST /v1/users — Create user

GET /v1/users/me — Current user profile

PATCH /v1/users/me — Update current user


### Connector Discovery &#x26; External Request Endpoints

GET /v1/connectors?q=&#x26;limit=&#x26;offset= — List connectors with keyword search

GET /v1/connectors/{dataProviderSlug} — Get connector detail (dataSources + actions)

GET /v1/connectors/{dataProviderSlug}/actions/{actionKey} — Get action detail (input/output schemas, API endpoint)

GET /v1/connectors/{dataProviderSlug}/connections — Get tenant connections + user connection status

PUT /v1/connectors/{dataProviderSlug} — Send HTTP request through connector provider auth


Scopes: `Read.All`, `ReadWrite.All`, or `Connectors.Read.All`. The `PUT` endpoint requires `ReadWrite.All`.

**PUT request body** for sending requests through a connector:

{

"endpoint": "relative/path/or/absolute-url",

"requestMethod": "GET",

"data": { "fields": "id,name", "limit": 20 },

"contentType": "application/json",

"headers": { "Authorization": "Bearer <override-token>" },

"connectionId": "optional-tenant-connection-uuid",

"connectionAccountId": "optional-connection-account-uuid"

}


The connector resolves auth credentials (OAuth tokens, base URL) from the provider configuration and stored connections. Custom `headers.Authorization` overrides the stored token.

### Action Run Endpoints

GET /v1/apps/base/actions — List base actions

GET /v1/apps/{appSlug}/actions/{actionSlug} — Get action metadata

POST /v1/apps/{appSlug}/actions/{actionSlug}/run — Run action directly


Action run accepts arbitrary JSON body as input. Optional headers: `x-connection-id`, `x-connection-account-id`.

### Studio (Dev) Schema Endpoints

The studio surface manages dev-app schema objects. Most routes are gated by the `Architect.Read.All` / `Architect.ReadWrite.All` scopes, and the app is identified by its tenant `app_id` UUID.

Apps (mutations only — list uses /v1/apps)

DELETE /v1/dev/apps/{appId} — Archive app

POST /v1/dev/apps/{appId}/restore — Restore archived app

DELETE /v1/dev/apps/{appId}/permanent — Permanently delete app

Data sources

GET /v1/dev/apps/{appId}/data-sources — List data sources (?expand=fields,...)

GET /v1/dev/apps/{appId}/data-sources/{dataSourceId} — Get data source

POST /v1/dev/apps/{appId}/data-sources — Create data source

PATCH /v1/dev/apps/{appId}/data-sources/{dataSourceId} — Update data source

DELETE /v1/dev/apps/{appId}/data-sources/{dataSourceId} — Archive data source

POST /v1/dev/apps/{appId}/data-sources/{dataSourceId}/restore — Restore archived data source

DELETE /v1/dev/apps/{appId}/data-sources/{dataSourceId}/permanent — Permanently delete data source

POST /v1/dev/apps/{appId}/data-sources/bulk — Bulk create (body: { dataSources })

Fields

GET /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields — List fields

GET /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId} — Get field

POST /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields — Create field

PATCH /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId} — Update field

DELETE /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId} — Delete field

POST /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/batch — Bulk create (body: { fields })

PATCH /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/batch — Bulk update (body: { fields[].fieldId })

DELETE /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/batch — Bulk delete (body: { fieldIds })

Field enums

GET /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId}/enums — List enum options

POST /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId}/enums — Create enums (body: { enums })

PATCH /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId}/enums — Update enums (body: { enums[].enumId })

DELETE /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId}/enums — Delete enums (body: { enumIds })

Data views (saved views) — slug-scoped

GET /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/views — List views

GET /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/views/{viewId} — Get view

POST /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/views — Create view

PUT /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/views/{viewId} — Update view

DELETE /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/views/{viewId} — Delete view

Forms (record-entry layouts) — slug-scoped

GET /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/forms — List forms

GET /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/forms/{formId} — Get form

POST /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/forms — Create form

PUT /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/forms/{formId} — Update form

DELETE /v1/apps/{appSlug}/data-sources/{dataSourceSlug}/forms/{formId} — Delete form

Webforms (public-facing forms)

GET /v1/dev/webforms — List webforms (?dataSourceId)

GET /v1/dev/webforms/{webformId} — Get webform

POST /v1/dev/webforms — Create webform

PATCH /v1/dev/webforms/{webformId} — Update webform

DELETE /v1/dev/webforms/{webformId} — Delete webform

HTML / PDF / DOCX export templates

GET /v1/dev/html-templates — List (?dataSourceId,&#x26;isDefault,&#x26;limit,&#x26;offset)

GET /v1/dev/html-templates/{templateId} — Get template

POST /v1/dev/html-templates — Create template

PUT /v1/dev/html-templates/{templateId} — Update template

DELETE /v1/dev/html-templates/{templateId} — Delete template

Email templates

GET /v1/dev/email-templates — List (?dataSourceId,&#x26;limit,&#x26;offset)

GET /v1/dev/email-templates/{templateId} — Get template

POST /v1/dev/email-templates — Create template

PUT /v1/dev/email-templates/{templateId} — Update template

DELETE /v1/dev/email-templates/{templateId} — Delete template


Notes:

- The bulk update DTOs do not mirror the list/get response shapes. Send `fields[].fieldId` and `enums[].enumId` (not `id`).

- A webform created without `dataSourceId` posts submissions into the tenant-schema `webform_record` table instead of a data source.

- Archived data sources cannot be reliably resolved by slug; use the ID for `restore` and `permanent` routes.

### Automation Endpoints

Tenant-app automation CRUD plus typed trigger and action node mutations. Gated by `Architect.Read.All` / `Architect.ReadWrite.All`.

Automations

GET /v1/dev/apps/{appId}/automations — List automations

GET /v1/dev/apps/{appId}/automations/{id} — Get automation (includes triggers)

POST /v1/dev/apps/{appId}/automations — Create automation + first trigger

PATCH /v1/dev/apps/{appId}/automations/{id} — Update automation (name, status, source_data_source_id)

DELETE /v1/dev/apps/{appId}/automations/{id} — Delete automation (204)

Triggers — typed create/update, type-independent delete

POST /v1/dev/apps/{appId}/automations/{automationId}/triggers/{type} — Create trigger

PATCH /v1/dev/apps/{appId}/automations/{automationId}/triggers/{type}/{triggerId} — Update trigger

DELETE /v1/dev/apps/{appId}/automations/{automationId}/triggers/{triggerId} — Delete trigger (204)

Action nodes — typed create/update, type-independent delete

GET /v1/dev/apps/{appId}/automations/{automationId}/nodes — List nodes

GET /v1/dev/apps/{appId}/automations/{automationId}/nodes/{nodeId} — Get node

POST /v1/dev/apps/{appId}/automations/{automationId}/nodes/{type} — Create node

PATCH /v1/dev/apps/{appId}/automations/{automationId}/nodes/{type}/{nodeId} — Update node

DELETE /v1/dev/apps/{appId}/automations/{automationId}/nodes/{nodeId} — Delete node (204)


Trigger `{type}` values (kebab-case URL segments): `record-created`, `record-modified`, `record-deleted`, `recurrence`, `app-event`, `webhook`, `emailhook`, `webform`, `button-activation`, `manual-activation`.

Action node `{type}` values: `external-action`, `send-email`, `send-notification`, `create-record`, `update-records`, `request-approval`, `request-input`, `http-request`, `data-source-query`, `custom-query`, `generate-document`, `ai-prompt`, `ai-agent`, `execute-script`.

`POST /v1/dev/apps/{appId}/automations` accepts `trigger_type` in camelCase (e.g. `recordCreated`, `recordModified`, `recordDeleted`, `recurrence`, `appEvent`, `webhook`, `emailhook`, `webform`, `buttonActivation`, `manualActivation`) on `CreateAutomationDto`. The typed trigger CRUD endpoints use the kebab-case form in the URL.

Request bodies use `snake_case` keys (e.g. `source_data_source_id`, `max_run_per_record`, `modified_columns`, `recurrence_frequency`, `core_data_provider_id`, `webhook_id`, `tenant_webform_id`, `action_type_id`, `field_mapping`, `dynamic_field_mapping`, `condition`, `input_template`, `input_transformer`, `custom_headers`, `pre_action_request`, `post_action_request`, `target_data_source_condition`).

Important: creating a node with `type=external-action` requires `action_type_id` (maps to `core_action.id`). The backend validates the supplied `data` against `core_action.input_json_schema` and inserts the linked `tenant_action` row in the same transaction.

### Action / Approval RPC (Production)

These are separate from the dev-app automation CRUD above. They drive the runtime engine.

PUT /v1/automation/processAction — Execute action payload (IActionPayload)

POST /v1/automation/exchange-rates (alias /v1/automation/syncExchangeRates) — Fetch/save FX rates (admin or api)

PUT /v1/automation/sendApprovalRequests — { approvalStatusFieldId, recordId }

PUT /v1/automation/sendApprovalResponse — Approve response

PUT /v1/automation/sendApprovalRevisionRequest — Approval revision request

PUT /v1/automation/sendPushNotification/{notificationId} — Push notification (admin or api)


### Messaging Endpoints

Tenant email accounts and transactional send. All routes require the `Messaging.Email.Send` OAuth2 scope.

GET /v1/messaging/email/accounts — List active tenant email accounts (no credentials)

POST /v1/messaging/email/accounts/{accountId}/send — Send email through an account


`POST /v1/messaging/email/accounts/{accountId}/send` body (`SendEmailDto`):

{

"to": ["user@example.com"],

"cc": ["manager@example.com"],

"bcc": ["audit@example.com"],

"replyTo": ["support@example.com"],

"subject": "Daily summary",

"body": "<p>Hello</p>",

"sendAsUser": false,

"attachments": [

{ "filePath": "records/abc/attachments/foo.pdf", "fileName": "foo.pdf", "mimeType": "application/pdf" }

]

}


Limits: `to`/`cc`/`bcc`/`replyTo` accept up to 50 RFC-5322 addresses each, subject is capped at 998 characters, body at 1 000 000 characters, attachments at 10 items, and `filePath` at 2048 characters. `sendAsUser` only takes effect when the account allows it (see `allowOverrideName` / `allowOverrideEmail` from the accounts list).

`EmailAccountDto` (returned by list) exposes: `id`, `name`, `provider`, `senderEmail`, `senderName`, `isUserAccessible`, `allowOverrideName`, `allowOverrideEmail`, `createdOn`. Credentials, tokens, and provider secrets are never returned.

`SendEmailResponseDto`: `{ messageId, provider, accepted, rejected }`.

### ACL / Role Management Endpoints

GET /v1/users/acl?dataSourceId={uuid}&#x26;recordId={uuid} — Read record ACL rows

POST /v1/users/acl/share — Upsert record shares

DELETE /v1/users/acl/share — Revoke record shares

PUT /v1/users/acl/owner — Transfer record ownership

GET /v1/users/acl/roles — List roles

GET /v1/users/acl/roles/{roleId} — Get one role

POST /v1/users/acl/roles — Create role

PATCH /v1/users/acl/roles/{roleId} — Update role

DELETE /v1/users/acl/roles/{roleId} — Delete role

GET /v1/users/acl/user-roles — List user-role assignments

GET /v1/users/acl/users/{userId}/roles — List one user's roles

POST /v1/users/acl/users/{userId}/roles — Add roles to a user

PUT /v1/users/acl/users/{userId}/roles — Replace a user's full role set

DELETE /v1/users/acl/users/{userId}/roles/{roleId} — Remove one role assignment

GET /v1/users/acl/role-queries — List role queries

GET /v1/users/acl/role-queries/{roleQueryId} — Get one role query

POST /v1/users/acl/role-queries — Create role query

PATCH /v1/users/acl/role-queries/{roleQueryId} — Update role query

DELETE /v1/users/acl/role-queries/{roleQueryId} — Delete role query


ACL routes require the normal authenticated API session, but they may not appear in generated Swagger/OpenAPI output because the backend currently excludes them from public docs. Integrate them with direct `RestApiClient` calls when you need record sharing, role CRUD, user-role assignment management, or role-query management.

For all ACL role operations, prefer using role `uid` values returned by the API. Nested role objects expose both `id` and `uid`, and both map to the role UID value.

### Making API Calls

// List items with query payload

const items = await client.get('/v1/apps/base/data-sources/project/items', {

columns: 'name, status, record_owner(firstname,lastname)',

filters: { rules: [{ field: 'status', operator: '!=', value: 'archived' }] },

orderBy: 'created_on DESC',

limit: 50,

})

// Get single item

const item = await client.get('/v1/apps/base/data-sources/project/items/uuid-here', {

columns: 'name, description, status',

})

// Create

const newItem = await client.post('/v1/apps/base/data-sources/project/items', {

name: 'New Project',

status: 'status-enum-id',

})

// Update

await client.patch('/v1/apps/base/data-sources/project/items/uuid-here', {

name: 'Updated Name',

})

// Delete

await client.delete('/v1/apps/base/data-sources/project/items/uuid-here')

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card