OpenAPI JSONMarkdown Docs

OpenAPI Explorer

Auto-generated OpenAPI definition for all enabled modules.

Default server: https://open-book-partners-erp.northbound.run/api

Authentication & Accounts

Showing 20 of 35 endpoints
GET/auth/admin/nav
Auth required

Resolve backend chrome bootstrap payload

Returns the backend chrome payload available to the authenticated administrator after applying scope, RBAC, role defaults, and personal sidebar preferences.

Responses

200Backend chrome payload
Content-Type: application/json
{
  "brand": null,
  "groups": [
    {
      "name": "string",
      "items": [
        {
          "href": "string",
          "title": "string"
        }
      ]
    }
  ],
  "settingsSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "settingsPathPrefixes": [
    "string"
  ],
  "profileSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "profilePathPrefixes": [
    "string"
  ],
  "grantedFeatures": [
    "string"
  ],
  "roles": [
    "string"
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/admin/nav" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/auth/autologin

Auto sign-in using env-configured demo credentials

When OM_AUTOLOGIN_EMAIL / OM_AUTOLOGIN_PASSWORD are configured, signs the visitor in with those credentials and redirects into the app. Intended for single-tenant demo instances only. Falls back to the login page when disabled or misconfigured.

Responses

200Success response
Content-Type: application/json
"string"
307Redirect into the app (or to /login on failure)
Content-Type: text/html
string

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/autologin" \
  -H "Accept: application/json"
POST/auth/feature-check
Auth required

Check feature grants for the current user

Evaluates which of the requested features are available to the signed-in user within the active tenant / organization context.

Request body (application/json)

{
  "features": [
    "string"
  ]
}

Responses

200Evaluation result
Content-Type: application/json
{
  "ok": true,
  "granted": [
    "string"
  ],
  "userId": "string"
}
400Invalid request — features array missing, too large, or contains invalid entries
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/feature-check" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"features\": [
    \"string\"
  ]
}"
GET/auth/features
Auth requiredauth.acl.manage

List declared feature flags

Returns all static features contributed by the enabled modules along with their module source. Requires features: auth.acl.manage

Responses

200Aggregated feature catalog
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "module": "string"
    }
  ],
  "modules": [
    {
      "id": "string",
      "title": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/features" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/auth/locale

Set locale and redirect

Stores the selected locale in a cookie and redirects to a safe local path.

Parameters

NameInRequiredSchemaDescription
localequeryYesany—
redirectqueryNoany—

Responses

200Success response
Content-Type: application/json
"string"
302Locale cookie set and request redirected
Content-Type: application/json
"string"
400Invalid locale
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/locale?locale=en" \
  -H "Accept: application/json"
POST/auth/locale

Set locale

Stores the selected locale in a cookie and returns a JSON success response.

Request body (application/json)

{
  "locale": "en"
}

Responses

200Locale cookie set
Content-Type: application/json
{
  "ok": true
}
400Invalid locale or malformed request body
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/locale" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"locale\": \"en\"
}"
POST/auth/login

Authenticate user credentials

Validates the submitted credentials and issues a bearer token cookie for subsequent API calls.

Request body (application/x-www-form-urlencoded)

email=user%40example.com&password=string

Responses

200Authentication succeeded
Content-Type: application/json
{
  "ok": true,
  "token": "string",
  "redirect": null
}
400Validation failed
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
401Invalid credentials
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
403User lacks required role
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
429Too many login attempts
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/login" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com&password=string"
POST/auth/logout
Auth required

Invalidate session and redirect

Clears authentication cookies and redirects the browser to the login page.

Responses

201Success response
Content-Type: application/json
"string"
302Redirect to login after successful logout
Content-Type: text/html
string

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/logout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/auth/profile
Auth required

Get current profile

Returns the email address for the signed-in user.

Responses

200Profile payload
Content-Type: application/json
{
  "email": "user@example.com",
  "roles": [
    "string"
  ]
}
404User not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/auth/profile
Auth required

Update current profile

Updates the email address or password for the signed-in user.

Request body (application/json)

{}

Responses

200Profile updated
Content-Type: application/json
{
  "ok": true,
  "email": "user@example.com"
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
POST/auth/reset

Send reset email

Requests a password reset email for the given account. The endpoint always returns `ok: true` to avoid leaking account existence.

Request body (application/x-www-form-urlencoded)

email=user%40example.com

Responses

200Reset email dispatched (or ignored for unknown accounts)
Content-Type: application/json
{
  "ok": true
}
400Invalid request origin
Content-Type: application/json
{
  "error": "string"
}
429Too many password reset requests
Content-Type: application/json
{
  "error": "string"
}
500Password reset email origin is not configured
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/reset" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com"
POST/auth/reset/confirm

Complete password reset

Validates the reset token and updates the user password.

Request body (application/x-www-form-urlencoded)

token=string&password=string

Responses

200Password reset succeeded
Content-Type: application/json
{
  "ok": true,
  "redirect": "string"
}
400Invalid token or payload
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
429Too many reset confirmation attempts
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/reset/confirm" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=string&password=string"
GET/auth/roles
Auth requiredauth.roles.list

List roles

Returns available roles within the current tenant. Super administrators receive visibility across tenants. Requires features: auth.roles.list

Parameters

NameInRequiredSchemaDescription
idqueryNoany—
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—
tenantIdqueryNoany—

Responses

200Role collection
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "usersCount": 1,
      "tenantId": null,
      "tenantName": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/roles?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/auth/roles
Auth requiredauth.roles.manage

Create role

Creates a new role anchored to the caller's tenant. Non-superadmins cannot target another tenant; supplying a foreign `tenantId` is rejected. Requires features: auth.roles.manage

Request body (application/json)

{
  "name": "string"
}

Responses

201Role created
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
PUT/auth/roles
Auth requiredauth.roles.manage

Update role

Updates mutable fields on an existing role. Requires features: auth.roles.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Role updated
Content-Type: application/json
{
  "ok": true
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}
404Role not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
DELETE/auth/roles
Auth requiredauth.roles.manage

Delete role

Deletes a role by identifier. Fails when users remain assigned. Requires features: auth.roles.manage

Parameters

NameInRequiredSchemaDescription
idqueryYesanyRole identifier

Responses

200Role deleted
Content-Type: application/json
{
  "ok": true
}
400Role cannot be deleted
Content-Type: application/json
{
  "error": "string"
}
404Role not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/auth/roles?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/auth/roles/acl
Auth requiredauth.acl.manage

Fetch role ACL

Returns the feature and organization assignments associated with a role within the current tenant. Requires features: auth.acl.manage

Parameters

NameInRequiredSchemaDescription
roleIdqueryYesany—
tenantIdqueryNoany—

Responses

200Role ACL entry
Content-Type: application/json
{
  "isSuperAdmin": true,
  "features": [
    "string"
  ],
  "organizations": null,
  "updatedAt": null
}
400Invalid role id
Content-Type: application/json
{
  "error": "string"
}
404Role not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/roles/acl?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/auth/roles/acl
Auth requiredauth.acl.manage

Update role ACL

Replaces the feature list, super admin flag, and optional organization assignments for a role. Requires features: auth.acl.manage

Request body (application/json)

{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "organizations": null
}

Responses

200Role ACL updated
Content-Type: application/json
{
  "ok": true,
  "sanitized": true
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}
404Role not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/auth/roles/acl" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizations\": null
}"
GET/auth/session/refresh

Refresh auth cookie from session token (browser)

Exchanges an existing `session_token` cookie for a fresh JWT auth cookie and redirects the browser.

Parameters

NameInRequiredSchemaDescription
redirectqueryNoanyAbsolute or relative URL to redirect after refresh

Responses

200Success response
Content-Type: application/json
"string"
302Redirect to target location when session is valid
Content-Type: text/html
string

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/session/refresh" \
  -H "Accept: application/json"
POST/auth/session/refresh

Refresh access token (API/mobile)

Exchanges a refresh token for a new JWT access token. Pass the refresh token obtained from login in the request body.

Request body (application/json)

{
  "refreshToken": "string"
}

Responses

200New access token issued
Content-Type: application/json
{
  "ok": true,
  "accessToken": "string",
  "expiresIn": 1
}
400Missing refresh token
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
401Invalid or expired token
Content-Type: application/json
{
  "ok": false,
  "error": "string"
}
429Too many refresh attempts
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/auth/session/refresh" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"refreshToken\": \"string\"
}"

Directory (Tenants & Organizations)

Showing 2 of 2 endpoints
GET/directory/organizations/lookup

Public organization lookup by slug

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/organizations/lookup" \
  -H "Accept: application/json"
GET/directory/tenants/lookup

Public tenant lookup

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/tenants/lookup" \
  -H "Accept: application/json"

API Documentation

Showing 1 of 1 endpoints
GET/version

Deployed Open Mercato version

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/version" \
  -H "Accept: application/json"

Audit & Action Logs

Showing 5 of 5 endpoints
GET/audit_logs/audit-logs/access
Auth requiredaudit_logs.view_self

Retrieve access logs

Fetches paginated access audit logs scoped to the authenticated user. Tenant administrators can optionally expand the search to other actors or organizations. Requires features: audit_logs.view_self

Parameters

NameInRequiredSchemaDescription
organizationIdqueryNoanyLimit results to a specific organization
actorUserIdqueryNoanyFilter by actor user id (tenant administrators only)
resourceKindqueryNoanyRestrict to a resource kind such as `order` or `product`
accessTypequeryNoanyAccess type filter, e.g. `read` or `export`
pagequeryNoanyPage number (default 1)
pageSizequeryNoanyPage size (default 50)
limitqueryNoanyExplicit maximum number of records when paginating manually
beforequeryNoanyReturn logs created before this ISO-8601 timestamp
afterqueryNoanyReturn logs created after this ISO-8601 timestamp

Responses

200Access logs returned successfully
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "resourceKind": "string",
      "resourceId": "string",
      "accessType": "string",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "fields": [
        "string"
      ],
      "context": null,
      "createdAt": "string"
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
400Invalid filters supplied
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/audit_logs/audit-logs/access" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/audit_logs/audit-logs/actions
Auth requiredaudit_logs.view_self

Fetch action logs

Returns recent action audit log entries. Tenant administrators can widen the scope to other actors or organizations, and callers can optionally restrict results to undoable actions. Requires features: audit_logs.view_self

Parameters

NameInRequiredSchemaDescription
organizationIdqueryNoanyLimit results to a specific organization
actorUserIdqueryNoanyFilter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list.
resourceKindqueryNoanyFilter by resource kind (e.g., "order", "product")
resourceIdqueryNoanyFilter by resource ID (UUID of the specific record)
actionTypequeryNoanyFilter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list.
fieldNamequeryNoanyFilter to entries where the given field changed. Accepts a single field name or a comma-separated list.
includeRelatedqueryNoanyWhen `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId
includeTotalqueryNoanyWhen `true`, the response includes the filtered total count.
undoableOnlyqueryNoanyWhen `true`, only undoable actions are returned
limitqueryNoanyMaximum number of records to return (default 50, max 1000)
offsetqueryNoanyZero-based record offset for pagination (legacy — prefer page/pageSize)
pagequeryNoanyPage number (default 1)
pageSizequeryNoanyPage size (default 50, max 200)
sortFieldqueryNoanySort field: `createdAt`, `user`, `action`, `field`, or `source`.
sortDirqueryNoanySort direction: `asc` or `desc`.
beforequeryNoanyReturn actions created before this ISO-8601 timestamp
afterqueryNoanyReturn actions created after this ISO-8601 timestamp

Responses

200Action logs retrieved successfully
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "commandId": "string",
      "actionLabel": null,
      "executionState": "done",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "resourceKind": null,
      "resourceId": null,
      "parentResourceKind": null,
      "parentResourceId": null,
      "undoToken": null,
      "createdAt": "string",
      "updatedAt": "string",
      "snapshotBefore": null,
      "snapshotAfter": null,
      "changes": null,
      "context": null
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
400Invalid filter values
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/audit_logs/audit-logs/actions?includeRelated=false&includeTotal=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/audit_logs/audit-logs/actions/export
Auth requiredaudit_logs.view_self

Export action logs as CSV

Returns a CSV attachment containing filtered action audit log entries. Tenant administrators can widen the scope to other actors or organizations. Requires features: audit_logs.view_self

Parameters

NameInRequiredSchemaDescription
organizationIdqueryNoanyLimit results to a specific organization
actorUserIdqueryNoanyFilter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list.
resourceKindqueryNoanyFilter by resource kind (e.g., "order", "product")
resourceIdqueryNoanyFilter by resource ID (UUID of the specific record)
actionTypequeryNoanyFilter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list.
fieldNamequeryNoanyFilter to entries where the given field changed. Accepts a single field name or a comma-separated list.
includeRelatedqueryNoanyWhen `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId
undoableOnlyqueryNoanyWhen `true`, only undoable actions are returned
limitqueryNoanyMaximum number of records to export (default 1000, capped at 1000)
sortFieldqueryNoanySort field: `createdAt`, `user`, `action`, `field`, or `source`.
sortDirqueryNoanySort direction: `asc` or `desc`.
beforequeryNoanyReturn actions created before this ISO-8601 timestamp
afterqueryNoanyReturn actions created after this ISO-8601 timestamp

Responses

200CSV export generated successfully
Content-Type: application/json
{
  "file": "csv"
}
400Invalid filter values
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/audit_logs/audit-logs/actions/export?includeRelated=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/audit_logs/audit-logs/actions/redo
Auth requiredaudit_logs.redo_self

Redo by action log id

Redoes the latest undone command owned by the caller. Requires the action to still be eligible for redo within tenant and organization scope. Requires features: audit_logs.redo_self

Request body (application/json)

{
  "logId": "string"
}

Responses

200Redo executed successfully
Content-Type: application/json
{
  "ok": true,
  "logId": null,
  "undoToken": null
}
400Log not eligible for redo
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/audit_logs/audit-logs/actions/redo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logId\": \"string\"
}"
POST/audit_logs/audit-logs/actions/undo
Auth requiredaudit_logs.undo_self

Undo action by token

Replays the undo handler registered for a command. The provided undo token must match the latest undoable log entry accessible to the caller. Requires features: audit_logs.undo_self

Request body (application/json)

{
  "undoToken": "string"
}

Responses

200Undo applied successfully
Content-Type: application/json
{
  "ok": true,
  "logId": "string"
}
400Invalid or unavailable undo token
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/audit_logs/audit-logs/actions/undo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"undoToken\": \"string\"
}"

Notifications

Showing 13 of 13 endpoints
GET/notifications
Auth required

List notifications

Returns a paginated collection of notifications.

Parameters

NameInRequiredSchemaDescription
statusqueryNoany—
typequeryNoany—
severityqueryNoany—
sourceEntityTypequeryNoany—
sourceEntityIdqueryNoany—
sincequeryNoany—
pagequeryNoany—
pageSizequeryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated notifications
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "title": "string",
      "body": null,
      "titleKey": null,
      "bodyKey": null,
      "titleVariables": null,
      "bodyVariables": null,
      "icon": null,
      "severity": "string",
      "status": "string",
      "actions": [
        {
          "id": "string",
          "label": "string"
        }
      ],
      "sourceModule": null,
      "sourceEntityType": null,
      "sourceEntityId": null,
      "linkHref": null,
      "createdAt": "string",
      "readAt": null,
      "actionTaken": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/notifications?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/notifications
Auth requirednotifications.create

Create notification

Creates a notification for a user. Requires features: notifications.create

Request body (application/json)

{
  "type": "string",
  "severity": "info",
  "recipientUserId": "00000000-0000-4000-8000-000000000000"
}

Responses

201Notification created
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"type\": \"string\",
  \"severity\": \"info\",
  \"recipientUserId\": \"00000000-0000-4000-8000-000000000000\"
}"
POST/notifications/{id}/action
Auth required

POST /notifications/{id}/action

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications/:id/action" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/notifications/{id}/dismiss
Auth required

PUT /notifications/{id}/dismiss

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/notifications/:id/dismiss" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/notifications/{id}/read
Auth required

PUT /notifications/{id}/read

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/notifications/:id/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/notifications/{id}/restore
Auth required

PUT /notifications/{id}/restore

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/notifications/:id/restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/notifications/batch
Auth requirednotifications.create

POST /notifications/batch

Requires features: notifications.create

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/notifications/feature
Auth requirednotifications.create

POST /notifications/feature

Requires features: notifications.create

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications/feature" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/notifications/mark-all-read
Auth required

PUT /notifications/mark-all-read

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/notifications/mark-all-read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/notifications/role
Auth requirednotifications.create

POST /notifications/role

Requires features: notifications.create

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications/role" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/notifications/settings
Auth requirednotifications.manage

GET /notifications/settings

Requires features: notifications.manage

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/notifications/settings
Auth requirednotifications.manage

POST /notifications/settings

Requires features: notifications.manage

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/notifications/unread-count
Auth required

GET /notifications/unread-count

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/notifications/unread-count" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Events

Showing 2 of 2 endpoints
GET/events
Auth requiredworkflows.view

List declared events

Returns every declared event. Filters: category, module, excludeTriggerExcluded (default true). Requires features: workflows.view

Responses

200Declared events
Content-Type: application/json
{
  "data": [
    {
      "id": "string",
      "label": "string"
    }
  ],
  "total": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/events" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/events/stream
Auth required

GET /events/stream

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/events/stream" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Integrations

Showing 8 of 8 endpoints
GET/integrations
Auth requiredintegrations.view

List integrations

Returns a paginated collection of integrations. Requires features: integrations.view

Parameters

NameInRequiredSchemaDescription
qqueryNoany—
categoryqueryNoany—
bundleIdqueryNoany—
isEnabledqueryNoany—
healthStatusqueryNoany—
sortqueryNoany—
orderqueryNoany—
pagequeryNoany—
pageSizequeryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated integrations
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "category": null,
      "tags": [
        "string"
      ],
      "hub": null,
      "providerKey": null,
      "bundleId": null,
      "author": null,
      "company": null,
      "version": null,
      "hasCredentials": true,
      "isEnabled": true,
      "apiVersion": null,
      "healthStatus": "healthy",
      "lastHealthCheckedAt": null,
      "lastHealthLatencyMs": null,
      "enabledAt": null,
      "analytics": {
        "lastActivityAt": null,
        "totalCount": 1,
        "errorCount": 1,
        "errorRate": 1,
        "dailyCounts": [
          1
        ]
      }
    }
  ],
  "total": 1,
  "totalPages": 1,
  "bundles": [
    {
      "id": "string",
      "title": "string",
      "description": "string",
      "icon": null,
      "integrationCount": 1,
      "enabledCount": 1
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/integrations?order=asc&page=1&pageSize=100" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/integrations/{id}
Auth requiredintegrations.view

Get integration detail

Requires features: integrations.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/integrations/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/integrations/{id}/credentials
Auth requiredintegrations.credentials.manage

Get or save integration credentials

Requires features: integrations.credentials.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/integrations/:id/credentials" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/integrations/{id}/credentials
Auth requiredintegrations.credentials.manage

Get or save integration credentials

Requires features: integrations.credentials.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/integrations/:id/credentials" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/integrations/{id}/health
Auth requiredintegrations.manage

Run health check for an integration

Requires features: integrations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/integrations/:id/health" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/integrations/{id}/state
Auth requiredintegrations.manage

Update integration state

Requires features: integrations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/integrations/:id/state" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/integrations/{id}/version
Auth requiredintegrations.manage

Change integration API version

Requires features: integrations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/integrations/:id/version" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/integrations/logs
Auth requiredintegrations.manage

List integration logs

Requires features: integrations.manage

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/integrations/logs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Data Sync

Showing 17 of 17 endpoints
GET/data_sync/mappings
Auth requireddata_sync.configure

List or create field mappings

Requires features: data_sync.configure

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/mappings
Auth requireddata_sync.configure

List or create field mappings

Requires features: data_sync.configure

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/mappings/{id}
Auth requireddata_sync.configure

Get, update, or delete a field mapping

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/data_sync/mappings/{id}
Auth requireddata_sync.configure

Get, update, or delete a field mapping

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/data_sync/mappings/{id}
Auth requireddata_sync.configure

Get, update, or delete a field mapping

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

204Success

No response body.

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/data_sync/mappings/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/options
Auth requireddata_sync.view

List data sync integration options

Requires features: data_sync.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/run
Auth requireddata_sync.run

Start a data sync run

Requires features: data_sync.run

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/run" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/runs
Auth requireddata_sync.view

List sync runs

Requires features: data_sync.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/runs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/runs/{id}
Auth requireddata_sync.view

Get sync run detail

Requires features: data_sync.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/runs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/runs/{id}/cancel
Auth requireddata_sync.run

Cancel a running sync

Requires features: data_sync.run

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/runs/:id/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/runs/{id}/retry
Auth requireddata_sync.run

Retry a failed sync run

Requires features: data_sync.run

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/runs/:id/retry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/schedules
Auth requireddata_sync.configure

List or create sync schedules

Requires features: data_sync.configure

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/schedules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/schedules
Auth requireddata_sync.configure

List or create sync schedules

Requires features: data_sync.configure

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/schedules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/data_sync/schedules/{id}
Auth requireddata_sync.configure

Manage a sync schedule

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/data_sync/schedules/{id}
Auth requireddata_sync.configure

Manage a sync schedule

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/data_sync/schedules/{id}
Auth requireddata_sync.configure

Manage a sync schedule

Requires features: data_sync.configure

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

204Success

No response body.

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/data_sync/schedules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/data_sync/validate
Auth requireddata_sync.configure

Validate sync connection

Requires features: data_sync.configure

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/data_sync/validate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Progress

Showing 6 of 6 endpoints
GET/progress/active
Auth requiredprogress.view

GET /progress/active

Requires features: progress.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/progress/active" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/progress/jobs
Auth requiredprogress.view

List progressjobs

Returns a paginated collection of progressjobs scoped to the authenticated tenant. Requires features: progress.view

Parameters

NameInRequiredSchemaDescription
statusqueryNoany—
jobTypequeryNoany—
parentJobIdqueryNoany—
includeCompletedqueryNoany—
completedSincequeryNoany—
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated progressjobs
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "jobType": "string",
      "name": "string",
      "description": null,
      "status": "string",
      "progressPercent": 1,
      "processedCount": 1,
      "totalCount": null,
      "etaSeconds": null,
      "cancellable": true,
      "startedAt": null,
      "finishedAt": null,
      "errorMessage": null,
      "createdAt": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": null
    }
  ],
  "total": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/progress/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/progress/jobs
Auth requiredprogress.create

Create progressjob

Creates a new progress job for tracking a long-running operation. Requires features: progress.create

Request body (application/json)

{
  "jobType": "string",
  "name": "string",
  "cancellable": false
}

Responses

201ProgressJob created
Content-Type: application/json
{
  "id": null
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/progress/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"jobType\": \"string\",
  \"name\": \"string\",
  \"cancellable\": false
}"
GET/progress/jobs/{id}
Auth requiredprogress.view

GET /progress/jobs/{id}

Requires features: progress.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/progress/jobs/{id}
Auth requiredprogress.update

PUT /progress/jobs/{id}

Requires features: progress.update

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/progress/jobs/{id}
Auth requiredprogress.cancel

DELETE /progress/jobs/{id}

Requires features: progress.cancel

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

204Success

No response body.

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Scheduler

Showing 8 of 8 endpoints
GET/scheduler/jobs
Auth requiredscheduler.jobs.view

List scheduledjobs

Returns a paginated collection of scheduledjobs scoped to the authenticated organization. Requires features: scheduler.jobs.view

Parameters

NameInRequiredSchemaDescription
pagequeryNoany—
pageSizequeryNoany—
idqueryNoany—
searchqueryNoany—
scopeTypequeryNoany—
isEnabledqueryYesany—
sourceTypequeryNoany—
sourceModulequeryNoany—
sortqueryNoany—
orderqueryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated scheduledjobs
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "description": null,
      "scopeType": "system",
      "organizationId": null,
      "tenantId": null,
      "scheduleType": "cron",
      "scheduleValue": "string",
      "timezone": "string",
      "targetType": "queue",
      "targetQueue": null,
      "targetCommand": null,
      "targetPayload": null,
      "requireFeature": null,
      "isEnabled": true,
      "lastRunAt": null,
      "nextRunAt": null,
      "sourceType": "user",
      "sourceModule": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/scheduler/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/scheduler/jobs
Auth requiredscheduler.jobs.manage

Create scheduledjob

Creates a new scheduled job with cron or interval-based scheduling. Requires features: scheduler.jobs.manage

Request body (application/json)

{
  "name": "string",
  "description": null,
  "scopeType": "system",
  "organizationId": null,
  "tenantId": null,
  "scheduleType": "cron",
  "scheduleValue": "string",
  "timezone": "UTC",
  "targetType": "queue",
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null,
  "isEnabled": true,
  "sourceType": "user",
  "sourceModule": null
}

Responses

201ScheduledJob created
Content-Type: application/json
{
  "id": null
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"scopeType\": \"system\",
  \"organizationId\": null,
  \"tenantId\": null,
  \"scheduleType\": \"cron\",
  \"scheduleValue\": \"string\",
  \"timezone\": \"UTC\",
  \"targetType\": \"queue\",
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null,
  \"isEnabled\": true,
  \"sourceType\": \"user\",
  \"sourceModule\": null
}"
PUT/scheduler/jobs
Auth requiredscheduler.jobs.manage

Update scheduledjob

Updates an existing scheduled job by ID. Requires features: scheduler.jobs.manage

Request body (application/json)

{
  "id": "string",
  "description": null,
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null
}

Responses

200ScheduledJob updated
Content-Type: application/json
{
  "ok": true
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"description\": null,
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null
}"
DELETE/scheduler/jobs
Auth requiredscheduler.jobs.manage

Delete scheduledjob

Deletes a scheduled job by ID. Requires features: scheduler.jobs.manage

Request body (application/json)

{
  "id": "string"
}

Responses

200ScheduledJob deleted
Content-Type: application/json
{
  "ok": true
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"
GET/scheduler/jobs/{id}/executions

Get execution history for a schedule

Fetch recent executions from BullMQ for a scheduled job. Requires QUEUE_STRATEGY=async.

Parameters

NameInRequiredSchemaDescription
idpathYesany—
pageSizequeryNoany—

Responses

200Execution history
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "scheduleId": "00000000-0000-4000-8000-000000000000",
      "startedAt": "string",
      "finishedAt": null,
      "status": "running",
      "triggerType": "scheduled",
      "triggeredByUserId": null,
      "errorMessage": null,
      "errorStack": null,
      "durationMs": null,
      "queueJobId": "string",
      "queueName": "string",
      "attemptsMade": 1,
      "result": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
400Local strategy not supported
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized
Content-Type: application/json
{
  "error": "string"
}
403Access denied
Content-Type: application/json
{
  "error": "string"
}
404Schedule not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/scheduler/jobs/:id/executions?pageSize=20" \
  -H "Accept: application/json"
GET/scheduler/queue-jobs/{jobId}

Get BullMQ job details and logs

Fetch detailed information and logs for a queue job. Requires QUEUE_STRATEGY=async.

Parameters

NameInRequiredSchemaDescription
jobIdpathYesany—
queuequeryYesany—

Responses

200Job details and logs
Content-Type: application/json
{
  "id": "string",
  "name": "string",
  "state": "waiting",
  "progress": null,
  "returnvalue": null,
  "failedReason": null,
  "stacktrace": null,
  "attemptsMade": 1,
  "processedOn": null,
  "finishedOn": null,
  "logs": [
    "string"
  ]
}
400Invalid request or local strategy not supported
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized
Content-Type: application/json
{
  "error": "string"
}
403Access denied
Content-Type: application/json
{
  "error": "string"
}
404Job not found
Content-Type: application/json
{
  "error": "string"
}
500Internal server error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/scheduler/queue-jobs/:jobId?queue=string" \
  -H "Accept: application/json"
GET/scheduler/targets

List available queues and commands

Returns all registered queue names (from module workers) and command IDs (from the command registry) that can be used as schedule targets.

Responses

200Available targets
Content-Type: application/json
{
  "queues": [
    {
      "value": "string",
      "label": "string"
    }
  ],
  "commands": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
401Unauthorized
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/scheduler/targets" \
  -H "Accept: application/json"
POST/scheduler/trigger

Manually trigger a schedule

Executes a scheduled job immediately, bypassing the scheduled time. Only works with async queue strategy.

Request body (application/json)

{
  "id": "string"
}

Responses

200Schedule triggered successfully
Content-Type: application/json
{
  "ok": true,
  "jobId": "string",
  "message": "string"
}
400Invalid request or local strategy not supported
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized
Content-Type: application/json
{
  "error": "string"
}
403Access denied
Content-Type: application/json
{
  "error": "string"
}
404Schedule not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/scheduler/trigger" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"

Currencies

Showing 14 of 14 endpoints
GET/currencies/currencies
Auth requiredcurrencies.view

List currencies

Returns a paginated collection of currencies scoped to the authenticated organization. Requires features: currencies.view

Parameters

NameInRequiredSchemaDescription
idqueryNoany—
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
isBasequeryNoany—
isActivequeryNoany—
codequeryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated currencies
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "code": "string",
      "name": "string",
      "symbol": null,
      "decimalPlaces": 1,
      "thousandsSeparator": null,
      "decimalSeparator": null,
      "isBase": true,
      "isActive": true,
      "createdAt": null,
      "updatedAt": null,
      "organizationId": "string",
      "tenantId": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/currencies/currencies?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/currencies/currencies
Auth requiredcurrencies.manage

Create currency

Creates a new currency. Requires features: currencies.manage

Request body (application/json)

{
  "organizationId": "string",
  "tenantId": "string",
  "code": "string",
  "name": "string",
  "symbol": null,
  "thousandsSeparator": null,
  "decimalSeparator": null
}

Responses

201Currency created
Content-Type: application/json
{
  "id": null
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"organizationId\": \"string\",
  \"tenantId\": \"string\",
  \"code\": \"string\",
  \"name\": \"string\",
  \"symbol\": null,
  \"thousandsSeparator\": null,
  \"decimalSeparator\": null
}"
PUT/currencies/currencies
Auth requiredcurrencies.manage

Update currency

Updates an existing currency by id. Requires features: currencies.manage

Request body (application/json)

{
  "id": "string",
  "symbol": null,
  "thousandsSeparator": null,
  "decimalSeparator": null
}

Responses

200Currency updated
Content-Type: application/json
{
  "ok": true
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"symbol\": null,
  \"thousandsSeparator\": null,
  \"decimalSeparator\": null
}"
DELETE/currencies/currencies
Auth requiredcurrencies.manage

Delete currency

Deletes a currency by id. Requires features: currencies.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Currency deleted
Content-Type: application/json
{
  "ok": true
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
GET/currencies/currencies/options
Auth requiredcurrencies.view

List currency options

Returns currencies formatted for select inputs. Requires features: currencies.view

Parameters

NameInRequiredSchemaDescription
qqueryNoany—
queryqueryNoany—
searchqueryNoany—
includeInactivequeryNoany—
limitqueryNoany—

Responses

200Option list
Content-Type: application/json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
400Invalid query
Content-Type: application/json
{
  "items": []
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/currencies/currencies/options?limit=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/currencies/exchange-rates
Auth requiredcurrencies.rates.view

List exchangerates

Returns a paginated collection of exchangerates scoped to the authenticated organization. Requires features: currencies.rates.view

Parameters

NameInRequiredSchemaDescription
idqueryNoany—
pagequeryNoany—
pageSizequeryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
fromCurrencyCodequeryNoany—
toCurrencyCodequeryNoany—
isActivequeryNoany—
sourcequeryNoany—
typequeryNoany—
idsqueryNoanyComma-separated list of record UUIDs to filter by (max 200).

Responses

200Paginated exchangerates
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fromCurrencyCode": "string",
      "toCurrencyCode": "string",
      "rate": "string",
      "date": "string",
      "source": "string",
      "type": null,
      "isActive": true,
      "createdAt": null,
      "updatedAt": null,
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "tenantId": "00000000-0000-4000-8000-000000000000"
    }
  ],
  "total": 1,
  "totalPages": 1
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/currencies/exchange-rates?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/currencies/exchange-rates
Auth requiredcurrencies.rates.manage

Create exchangerate

Creates a new exchange rate. Requires features: currencies.rates.manage

Request body (application/json)

{
  "organizationId": "string",
  "tenantId": "string",
  "fromCurrencyCode": "string",
  "toCurrencyCode": "string",
  "rate": "string",
  "type": null
}

Responses

201ExchangeRate created
Content-Type: application/json
{
  "id": null
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"organizationId\": \"string\",
  \"tenantId\": \"string\",
  \"fromCurrencyCode\": \"string\",
  \"toCurrencyCode\": \"string\",
  \"rate\": \"string\",
  \"type\": null
}"
PUT/currencies/exchange-rates
Auth requiredcurrencies.rates.manage

Update exchangerate

Updates an existing exchange rate by id. Requires features: currencies.rates.manage

Request body (application/json)

{
  "id": "string",
  "type": null
}

Responses

200ExchangeRate updated
Content-Type: application/json
{
  "ok": true
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"type\": null
}"
DELETE/currencies/exchange-rates
Auth requiredcurrencies.rates.manage

Delete exchangerate

Deletes an exchange rate by id. Requires features: currencies.rates.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200ExchangeRate deleted
Content-Type: application/json
{
  "ok": true
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
GET/currencies/fetch-configs
Auth requiredcurrencies.fetch.view

List currency fetch configurations

Returns all currency fetch configurations scoped to the authenticated organization. Requires features: currencies.fetch.view

Responses

200A list of currency fetch configurations
Content-Type: application/json
{
  "configs": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "provider": "string",
      "isEnabled": true,
      "syncTime": null,
      "lastSyncAt": null,
      "lastSyncStatus": null,
      "lastSyncMessage": null,
      "lastSyncCount": null,
      "config": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/currencies/fetch-configs
Auth requiredcurrencies.fetch.manage

Create currency fetch configuration

Creates a new currency fetch configuration. Requires features: currencies.fetch.manage

Request body (application/json)

{
  "provider": "NBP",
  "isEnabled": false,
  "syncTime": null,
  "config": null
}

Responses

201Currency fetch configuration created successfully
Content-Type: application/json
{
  "config": {
    "id": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "provider": "string",
    "isEnabled": true,
    "syncTime": null,
    "lastSyncAt": null,
    "lastSyncStatus": null,
    "lastSyncMessage": null,
    "lastSyncCount": null,
    "config": null,
    "createdAt": "string",
    "updatedAt": "string"
  }
}
400Bad request
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"provider\": \"NBP\",
  \"isEnabled\": false,
  \"syncTime\": null,
  \"config\": null
}"
PUT/currencies/fetch-configs
Auth requiredcurrencies.fetch.manage

Update currency fetch configuration

Updates an existing currency fetch configuration by id. Requires features: currencies.fetch.manage

Request body (application/json)

{
  "syncTime": null,
  "config": null,
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Currency fetch configuration updated successfully
Content-Type: application/json
{
  "config": {
    "id": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "provider": "string",
    "isEnabled": true,
    "syncTime": null,
    "lastSyncAt": null,
    "lastSyncStatus": null,
    "lastSyncMessage": null,
    "lastSyncCount": null,
    "config": null,
    "createdAt": "string",
    "updatedAt": "string"
  }
}
400Bad request
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"syncTime\": null,
  \"config\": null,
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
DELETE/currencies/fetch-configs
Auth requiredcurrencies.fetch.manage

Delete currency fetch configuration

Deletes a currency fetch configuration by id. Requires features: currencies.fetch.manage

Parameters

NameInRequiredSchemaDescription
idqueryYesanyCurrency fetch configuration identifier to delete

Responses

200Currency fetch configuration deleted successfully
Content-Type: application/json
{
  "success": true
}
400Bad request
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/currencies/fetch-configs?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/currencies/fetch-rates

Fetch currency rates

Fetches currency exchange rates from configured providers for a specific date.

Request body (application/json)

{}

Responses

200Currency rates fetched successfully
Content-Type: application/json
{
  "totalFetched": 1,
  "byProvider": {
    "key": {
      "count": 1
    }
  },
  "errors": [
    "string"
  ]
}
400Bad request
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized
Content-Type: application/json
{
  "error": "string"
}
500Internal server error
Content-Type: application/json
{
  "totalFetched": 1,
  "byProvider": {
    "key": {
      "count": 1
    }
  },
  "errors": [
    "string"
  ]
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/currencies/fetch-rates" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"

Attachments

Showing 14 of 14 endpoints
GET/attachments
Auth requiredattachments.view

List attachments for a record

Returns uploaded attachments for the given entity record, ordered by newest first. Requires features: attachments.view

Parameters

NameInRequiredSchemaDescription
entityIdqueryYesanyEntity identifier that owns the attachments
recordIdqueryYesanyRecord identifier within the entity
pagequeryNoany—
pageSizequeryNoany—

Responses

200Attachments found for the record
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "url": "string",
      "fileName": "string",
      "fileSize": 1,
      "createdAt": "string",
      "mimeType": null,
      "content": null
    }
  ]
}
400Missing entity or record identifiers
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments?entityId=string&recordId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/attachments
Auth requiredattachments.manage

Upload attachment

Uploads a new attachment using multipart form-data and stores metadata for later retrieval. Requires features: attachments.manage

Request body (multipart/form-data)

entityId=string
recordId=string
file=string

Responses

200Attachment stored successfully
Content-Type: application/json
{
  "ok": true,
  "item": {
    "id": "string",
    "url": "string",
    "fileName": "string",
    "fileSize": 1,
    "content": null
  }
}
400Payload validation error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: multipart/form-data" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"file\": \"string\"
}"
DELETE/attachments
Auth requiredattachments.manage

Delete attachment

Removes an uploaded attachment and deletes the stored asset. Requires features: attachments.manage

Parameters

NameInRequiredSchemaDescription
idqueryYesany—

Responses

200Attachment deleted
Content-Type: application/json
{
  "ok": true
}
400Missing attachment identifier
Content-Type: application/json
{
  "error": "string"
}
404Attachment not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/attachments?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/attachments/file/{id}

Download or serve attachment file

Returns the raw file content for an attachment. Path parameter: {id} - Attachment UUID. Query parameter: ?download=1 - Force file download with Content-Disposition header. Access control is enforced based on partition settings.

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200File content with appropriate MIME type
Content-Type: application/json
"string"
400Missing attachment ID
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized - authentication required for private partitions
Content-Type: application/json
{
  "error": "string"
}
403Forbidden - insufficient permissions
Content-Type: application/json
{
  "error": "string"
}
404Attachment or file not found
Content-Type: application/json
{
  "error": "string"
}
500Partition misconfigured
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments/file/:id" \
  -H "Accept: application/json"
GET/attachments/image/{id}/{slug}

Serve image with optional resizing

Returns an image attachment with optional on-the-fly resizing and cropping. Resized images are cached for performance. Only works with image MIME types. Path parameter: {id} - Attachment UUID. Query parameters: ?width=N (1-4000 pixels), ?height=N (1-4000 pixels), ?cropType=cover|contain (resize behavior).

Parameters

NameInRequiredSchemaDescription
idpathYesany—
slugpathNoany—

Responses

200Binary image content (Content-Type: image/jpeg, image/png, etc.)
Content-Type: application/json
"string"
400Invalid parameters, missing ID, or non-image attachment
Content-Type: application/json
{
  "error": "string"
}
401Unauthorized - authentication required for private partitions
Content-Type: application/json
{
  "error": "string"
}
403Forbidden - insufficient permissions
Content-Type: application/json
{
  "error": "string"
}
404Image not found
Content-Type: application/json
{
  "error": "string"
}
500Partition misconfigured or image rendering failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments/image/:id/:slug" \
  -H "Accept: application/json"
GET/attachments/library
Auth requiredattachments.view

List attachments

Returns paginated list of attachments with optional filtering by search term, partition, and tags. Includes available tags and partitions. Requires features: attachments.view

Parameters

NameInRequiredSchemaDescription
pagequeryNoanyPage number for pagination
pageSizequeryNoanyNumber of items per page (max 100)
searchqueryNoanySearch by file name (case-insensitive)
partitionqueryNoanyFilter by partition code
tagsqueryNoanyFilter by tags (comma-separated)
sortFieldqueryNoanyField to sort by
sortDirqueryNoanySort direction

Responses

200Attachments list with pagination and metadata
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fileName": "string",
      "fileSize": 1,
      "mimeType": "string",
      "partitionCode": "string",
      "partitionTitle": null,
      "url": null,
      "createdAt": "string",
      "tags": [
        "string"
      ],
      "assignments": [],
      "content": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "availableTags": [
    "string"
  ],
  "partitions": [
    {
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true
    }
  ]
}
400Invalid query parameters
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments/library?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/attachments/library/{id}
Auth requiredattachments.view

Get attachment details

Returns complete details of an attachment including metadata, tags, assignments, and custom fields. Requires features: attachments.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Attachment details
Content-Type: application/json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "fileName": "string",
    "fileSize": 1,
    "mimeType": "string",
    "partitionCode": "string",
    "partitionTitle": null,
    "tags": [
      "string"
    ],
    "assignments": [],
    "content": null,
    "customFields": null
  }
}
400Invalid attachment ID
Content-Type: application/json
{
  "error": "string"
}
404Attachment not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PATCH/attachments/library/{id}
Auth requiredattachments.manage

Update attachment metadata

Updates attachment tags, assignments, and custom fields. Emits CRUD side effects for indexing and events. Requires features: attachments.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Request body (application/json)

{}

Responses

200Attachment updated successfully
Content-Type: application/json
{
  "ok": true
}
400Invalid payload or attachment ID
Content-Type: application/json
{
  "error": "string"
}
404Attachment not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to save attributes
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
DELETE/attachments/library/{id}
Auth requiredattachments.manage

Delete attachment

Permanently deletes an attachment file from storage and database. Emits CRUD side effects. Requires features: attachments.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Attachment deleted successfully
Content-Type: application/json
{
  "ok": true
}
400Invalid attachment ID
Content-Type: application/json
{
  "error": "string"
}
404Attachment not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/attachments/partitions
Auth requiredattachments.manage

List all attachment partitions

Returns all configured attachment partitions with storage settings, OCR configuration, and access control settings. Requires features: attachments.manage

Responses

200List of partitions
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true,
      "requiresOcr": true,
      "ocrModel": null,
      "configJson": null,
      "createdAt": null,
      "updatedAt": null,
      "envKey": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/attachments/partitions
Auth requiredattachments.manage

Create new partition

Creates a new attachment partition with specified storage and OCR settings. Requires unique partition code. Requires features: attachments.manage

Request body (application/json)

{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null
}

Responses

201Partition created successfully
Content-Type: application/json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
400Invalid payload or partition code
Content-Type: application/json
{
  "error": "string"
}
409Partition code already exists
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null
}"
PUT/attachments/partitions
Auth requiredattachments.manage

Update partition

Updates an existing partition. Partition code cannot be changed. Title, description, OCR settings, and access control can be modified. Requires features: attachments.manage

Request body (application/json)

{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null,
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Partition updated successfully
Content-Type: application/json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
400Invalid payload or code change attempt
Content-Type: application/json
{
  "error": "string"
}
404Partition not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null,
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
DELETE/attachments/partitions
Auth requiredattachments.manage

Delete partition

Deletes a partition. Default partitions cannot be deleted. Partitions with existing attachments cannot be deleted. Requires features: attachments.manage

Responses

200Partition deleted successfully
Content-Type: application/json
{
  "ok": true
}
400Invalid ID or default partition deletion attempt
Content-Type: application/json
{
  "error": "string"
}
404Partition not found
Content-Type: application/json
{
  "error": "string"
}
409Partition in use
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/attachments/transfer
Auth requiredattachments.manage

Transfer attachments to different record

Transfers one or more attachments from one record to another within the same entity type. Updates attachment assignments and metadata to reflect the new record. Requires features: attachments.manage

Request body (application/json)

{
  "entityId": "string",
  "attachmentIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "toRecordId": "string"
}

Responses

200Attachments transferred successfully
Content-Type: application/json
{
  "ok": true,
  "updated": 1
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}
404Attachments not found
Content-Type: application/json
{
  "error": "string"
}
500Attachment model missing
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/attachments/transfer" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"attachmentIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ],
  \"toRecordId\": \"string\"
}"

S3-Compatible Storage

Showing 5 of 5 endpoints
DELETE/storage-providers/s3/delete

Delete file from S3

Responses

204Success

No response body.

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/storage-providers/s3/delete" \
  -H "Accept: application/json"
GET/storage-providers/s3/download

Download file from S3

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/storage-providers/s3/download" \
  -H "Accept: application/json"
GET/storage-providers/s3/list

List S3 objects

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/storage-providers/s3/list" \
  -H "Accept: application/json"
POST/storage-providers/s3/signed-url

Generate S3 pre-signed URL

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/storage-providers/s3/signed-url" \
  -H "Accept: application/json"
POST/storage-providers/s3/upload

Upload file to S3

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/storage-providers/s3/upload" \
  -H "Accept: application/json"

API Keys

Showing 3 of 3 endpoints
GET/api_keys/keys
Auth requiredapi_keys.view

List API keys

Returns paginated API keys visible to the current user, including per-key role assignments and organization context. Requires features: api_keys.view

Parameters

NameInRequiredSchemaDescription
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—

Responses

200Collection of API keys
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "name": "string",
      "description": null,
      "keyPrefix": "string",
      "organizationId": null,
      "organizationName": null,
      "createdAt": "string",
      "lastUsedAt": null,
      "expiresAt": null,
      "roles": [
        {
          "id": "string",
          "name": null
        }
      ]
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
400Tenant context missing
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/api_keys/keys
Auth requiredapi_keys.create

Create API key

Creates a new API key, returning the one-time secret value together with the generated key prefix and scope details. Requires features: api_keys.create

Request body (application/json)

{
  "name": "string",
  "description": null,
  "tenantId": null,
  "organizationId": null,
  "roles": [],
  "expiresAt": null
}

Responses

201API key created successfully
Content-Type: application/json
{
  "id": "string",
  "name": "string",
  "keyPrefix": "string",
  "tenantId": null,
  "organizationId": null,
  "roles": [
    {
      "id": "string",
      "name": null
    }
  ]
}
400Invalid payload or missing tenant context
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"tenantId\": null,
  \"organizationId\": null,
  \"roles\": [],
  \"expiresAt\": null
}"
DELETE/api_keys/keys
Auth requiredapi_keys.delete

Delete API key

Removes an API key by identifier. The key must belong to the current tenant and fall within the requester organization scope. Requires features: api_keys.delete

Parameters

NameInRequiredSchemaDescription
idqueryYesanyAPI key identifier to delete

Responses

200Key deleted successfully
Content-Type: application/json
{
  "success": true
}
400Missing or invalid identifier
Content-Type: application/json
{
  "error": "string"
}
404Key not found within scope
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/api_keys/keys?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Feature Toggles

Showing 12 of 12 endpoints
GET/feature_toggles/check/boolean
Auth required

Check if feature is enabled

Checks if a feature toggle is enabled for the current context.

Parameters

NameInRequiredSchemaDescription
identifierqueryYesanyFeature toggle identifier

Responses

200Feature status
Content-Type: application/json
{
  "enabled": true,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
400Bad Request
Content-Type: application/json
{
  "error": "string"
}
404Tenant not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/check/boolean?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/check/json
Auth required

Get json config

Gets the json configuration for a feature toggle.

Parameters

NameInRequiredSchemaDescription
identifierqueryYesanyFeature toggle identifier

Responses

200Json config
Content-Type: application/json
{
  "valueType": "json",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
400Bad Request
Content-Type: application/json
{
  "error": "string"
}
404Tenant not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/check/json?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/check/number
Auth required

Get number config

Gets the number configuration for a feature toggle.

Parameters

NameInRequiredSchemaDescription
identifierqueryYesanyFeature toggle identifier

Responses

200Number config
Content-Type: application/json
{
  "valueType": "number",
  "value": 1,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
400Bad Request
Content-Type: application/json
{
  "error": "string"
}
404Tenant not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/check/number?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/check/string
Auth required

Get string config

Gets the string configuration for a feature toggle.

Parameters

NameInRequiredSchemaDescription
identifierqueryYesanyFeature toggle identifier

Responses

200String config
Content-Type: application/json
{
  "valueType": "string",
  "value": "string",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
400Bad Request
Content-Type: application/json
{
  "error": "string"
}
404Tenant not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/check/string?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/global
Auth requiredfeature_toggles.view

List global feature toggles

Returns all global feature toggles with filtering and pagination. Requires superadmin role. Requires features: feature_toggles.view

Parameters

NameInRequiredSchemaDescription
pagequeryNoanyPage number for pagination
pageSizequeryNoanyNumber of items per page (max 200)
searchqueryNoanyCase-insensitive search across identifier, name, description, and category
typequeryNoanyFilter by toggle type (boolean, string, number, json)
categoryqueryNoanyFilter by category (case-insensitive partial match)
namequeryNoanyFilter by name (case-insensitive partial match)
identifierqueryNoanyFilter by identifier (case-insensitive partial match)
sortFieldqueryNoanyField to sort by
sortDirqueryNoanySort direction (ascending or descending)

Responses

200Feature toggles collection
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "description": null,
      "category": null,
      "type": "boolean",
      "defaultValue": null,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
400Invalid query parameters
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/global?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/feature_toggles/global
Auth requiredfeature_toggles.global.manage

Create global feature toggle

Creates a new global feature toggle. Requires superadmin role. Requires features: feature_toggles.global.manage

Request body (application/json)

{
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null
}

Responses

201Feature toggle created
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"identifier\": \"string\",
  \"name\": \"string\",
  \"description\": null,
  \"category\": null,
  \"type\": \"boolean\",
  \"defaultValue\": null
}"
PUT/feature_toggles/global
Auth requiredfeature_toggles.global.manage

Update global feature toggle

Updates an existing global feature toggle. Requires superadmin role. Requires features: feature_toggles.global.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000",
  "description": null,
  "category": null,
  "defaultValue": null
}

Responses

200Feature toggle updated
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}
404Feature toggle not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"description\": null,
  \"category\": null,
  \"defaultValue\": null
}"
DELETE/feature_toggles/global
Auth requiredfeature_toggles.global.manage

Delete global feature toggle

Soft deletes a global feature toggle by ID. Requires superadmin role. Requires features: feature_toggles.global.manage

Parameters

NameInRequiredSchemaDescription
idqueryYesanyFeature toggle identifier

Responses

200Feature toggle deleted
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Invalid identifier
Content-Type: application/json
{
  "error": "string"
}
404Feature toggle not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/feature_toggles/global?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/global/{id}
Auth requiredfeature_toggles.view

Fetch feature toggle by ID

Returns complete details of a feature toggle. Requires features: feature_toggles.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Feature toggle detail
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null,
  "createdAt": null,
  "updatedAt": null
}
400Invalid identifier
Content-Type: application/json
{
  "error": "string"
}
404Feature toggle not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/global/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/global/{id}/override
Auth requiredfeature_toggles.view

Fetch feature toggle override

Returns feature toggle override. Requires features: feature_toggles.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Feature toggle overrides
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "tenantName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "toggleType": "boolean",
  "updatedAt": null
}
400Invalid request
Content-Type: application/json
{
  "error": "string"
}
404Feature toggle not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/global/:id/override" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/feature_toggles/overrides
Auth requiredfeature_toggles.view

List overrides

Returns list of feature toggle overrides. Requires features: feature_toggles.view

Parameters

NameInRequiredSchemaDescription
categoryqueryNoany—
namequeryNoany—
identifierqueryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
pagequeryNoany—
pageSizequeryNoany—

Responses

200List of overrides
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "toggleId": "00000000-0000-4000-8000-000000000000",
      "tenantName": "string",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "category": "string",
      "isOverride": true
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "isSuperAdmin": true
}
400Invalid query parameters
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/feature_toggles/overrides?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/feature_toggles/overrides
Auth requiredfeature_toggles.manage

Change override state

Enable, disable or inherit a feature toggle for a specific tenant. Requires features: feature_toggles.manage

Request body (application/json)

{
  "toggleId": "00000000-0000-4000-8000-000000000000",
  "isOverride": true
}

Responses

200Override updated
Content-Type: application/json
{
  "ok": true,
  "overrideToggleId": null
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Not found
Content-Type: application/json
{
  "error": "string"
}
500Internal server error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/feature_toggles/overrides" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"toggleId\": \"00000000-0000-4000-8000-000000000000\",
  \"isOverride\": true
}"

Advisory Inbox

Showing 1 of 1 endpoints
GET/advisory/organizations
Auth requiredadvisory.view

GET /advisory/organizations

Requires features: advisory.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/advisory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Ledger

Showing 1 of 1 endpoints
GET/ledger/entities
Auth requiredledger.view

Latest-period figures per account, one column per entity

Requires features: ledger.view

Responses

200Figures
Content-Type: application/json
{
  "seeded": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/ledger/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Model

Showing 20 of 23 endpoints
GET/model/engine
Auth requiredmodel.view

Recompute derived subtotals from components (reconciled vs imported) and assert the balance sheet balances every period

Requires features: model.view

Responses

200Engine run
Content-Type: application/json
{
  "seeded": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/engine" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/export
Auth requiredmodel.view

Download the seeded model as an Excel workbook (values + live formulas for subtotals)

Requires features: model.view

Responses

200XLSX file
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/export" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/metrics
Auth requiredmodel.view

ARR, NRR/GRR, Quick Ratio, Net New ARR, Rule of 40, Magic Number, Burn Multiple and Runway, each with provenance

Requires features: model.view

Responses

200SaaS metrics
Content-Type: application/json
{
  "seeded": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/metrics" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/overview
Auth requiredmodel.view

Assembled statements, ARR bridge and computed KPIs for the seeded model

Requires features: model.view

Responses

200Model overview
Content-Type: application/json
{
  "seeded": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/overview" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/accounts
Auth requiredmodel.view

The organization's QuickBooks accounts and subtotals a mapping can name, or why there are none yet

Requires features: model.view

Responses

200Options, or the figures’ own status
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/accounts" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/model/workbook/differences
Auth requiredmodel.manage

Upsert the confirmed classification and cause for one difference, keyed on the file pair and the difference key

Requires features: model.manage

Responses

200The stored note
Content-Type: application/json
{
  "note": {
    "differenceKey": "string",
    "classification": "string",
    "cause": "string",
    "confirmedAt": "string",
    "confirmedByUserId": null,
    "updatedAt": "string"
  }
}
400No organization selected, or an incomplete or invalid note
Content-Type: application/json
{
  "error": "string"
}
409The note was saved against a stale version (optimistic-lock conflict)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/model/workbook/differences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/model/workbook/differences
Auth requiredmodel.manage

Clear the confirmed answer for one difference, keyed on the file pair and the difference key

Requires features: model.manage

Responses

200Count cleared: 0 when this organization had no such note
Content-Type: application/json
{
  "deleted": 1
}
400No organization selected, or an incomplete or invalid key
Content-Type: application/json
{
  "error": "string"
}
409A note was cleared against a stale version (optimistic-lock conflict)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/model/workbook/differences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/model/workbook/inputs/{sourceKey}
Auth requiredmodel.manage

Upload one tracked source, read on arrival (multipart/form-data: `file`, and `month` as YYYY-MM)

Tracked sources only: `invoices_and_payments`, `headcount`, `hubspot_pipeline`, `hubspot_closed_won`, `ltv_cac`, `budget`, `ar_aging_detail`, `ttm_by_department`, `budget_to_actual`, `close_backup`, `close_sop`, `staff_model_workbook` and `staff_quickbooks_export`. A consumed source (the model workbook, the QuickBooks rows) and the QuickBooks report kinds are refused — the latter go through POST /api/qbo_reports/imports. The `x-obp-organization` header must name the organization the page shows, which must be the one selected. `month` is the month the file is for. A file of the month’s reporting package must send one; the close SOP, the annual budget and the team’s workbook take one when given and need none. A package file must be what its row holds (`.docx` for the close SOP, `.xlsx` for every other), and every QuickBooks report inside it must name this organization’s own QuickBooks company. A file that is refused is answered 400 with its problem list, and is not stored. A re-upload replaces the source’s row and removes the file it superseded; a superseded file that cannot be deleted is logged server-side and still answers 201, the upload itself having succeeded. Requires features: model.manage

Parameters

NameInRequiredSchemaDescription
sourceKeypathYesany—

Responses

201The stored source: its summary, the date it states, the month it is for and what it was recognised as
Content-Type: application/json
{}
400No organization selected or none stated in `x-obp-organization`, a consumed or QuickBooks-report source, no file, an extension the row does not take, no month for a package file or a month that is not YYYY-MM, a file whose expansion is unbounded, or a refused file (another row’s file, another company’s report, or its own parser’s problems)
Content-Type: application/json
{
  "error": "string"
}
404No source is tracked under that key
Content-Type: application/json
{
  "error": "string"
}
409The `x-obp-organization` header names another organization than the one selected: the page was loaded for another client
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
413Upload exceeds 25 MB
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/model/workbook/inputs/:sourceKey" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/model/workbook/inputs/{sourceKey}
Auth requiredmodel.manage

Remove the stored file for one tracked source, and the row that points at it

Tracked sources only, gated exactly as POST is, the `x-obp-organization` header included. 404 when this organization has no upload for that source — including on a repeated DELETE, and including another organization’s upload, which is not found rather than removed. A stored file that cannot be deleted is logged and does not fail the request: the row, which is what the board shows, is gone either way. Requires features: model.manage

Parameters

NameInRequiredSchemaDescription
sourceKeypathYesany—

Responses

204Removed: the row and its stored file

No response body.

400No organization selected or none stated in `x-obp-organization`, or a consumed or QuickBooks-report source, which this route does not serve
Content-Type: application/json
{
  "error": "string"
}
404No source is tracked under that key, or this organization has no upload for it
Content-Type: application/json
{
  "error": "string"
}
409The `x-obp-organization` header names another organization than the one selected: the page was loaded for another client
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/model/workbook/inputs/:sourceKey" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/model/workbook/mappings
Auth requiredmodel.manage

Upsert one or more confirmed mappings, each keyed on (statement, fingerprint)

Requires features: model.manage

Responses

200Count saved
Content-Type: application/json
{
  "saved": 1
}
400No organization selected, or invalid mappings
Content-Type: application/json
{
  "error": "string"
}
409A named row was saved against a stale version (optimistic-lock conflict)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/model/workbook/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/model/workbook/mappings
Auth requiredmodel.manage

Forget one or more saved mappings, each keyed on (statement, fingerprint) — the row is matched automatically again

Requires features: model.manage

Responses

200Count deleted
Content-Type: application/json
{
  "deleted": 1
}
400No organization selected, or invalid input
Content-Type: application/json
{
  "error": "string"
}
409A named row was deleted against a stale version (optimistic-lock conflict)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/model/workbook/mappings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/runs
Auth requiredmodel.view

List the organization's workbook update runs, newest first

Requires features: model.view

Responses

200Runs
Content-Type: application/json
{
  "items": [
    {}
  ]
}
400No organization selected, or an invalid limit
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/runs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/model/workbook/runs
Auth requiredmodel.manage

Start a workbook update run from an uploaded .xlsx or a stored run (multipart/form-data)

Requires features: model.manage

Responses

201The run (may be status: failed)
Content-Type: application/json
{}
400No organization, no stated organization for an uploaded file, no file/fromRunId, or a non-.xlsx upload
Content-Type: application/json
{
  "error": "string"
}
404fromRunId names no run of this organization with a stored file
Content-Type: application/json
{
  "error": "string"
}
413Upload exceeds 25 MB
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/model/workbook/runs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/runs/{id}
Auth requiredmodel.view

One run, in full: layout, figures, tie-outs, mapping review, changes

Requires features: model.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Run detail
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such run in this organization, or a malformed id
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/runs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/runs/{id}/file
Auth requiredmodel.view

Stream a run's output (default), source, or compared-staff-workbook .xlsx bytes

Requires features: model.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200XLSX file
Content-Type: application/json
"string"
400No organization selected, or a kind that is not output, source or staff
Content-Type: application/json
{
  "error": "string"
}
404No such run, it kept no file of that kind (including a run that compared nothing), or a malformed id
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/runs/:id/file" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/sessions
Auth requiredmodel.view

The organization's open workbook update draft and its input progress, or null

Requires features: model.view

Responses

200The open draft and its progress, both null when none is open, and the `organizationId` that answered
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/sessions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/model/workbook/sessions
Auth requiredmodel.manage

Open (or replace) the draft from an uploaded .xlsx (multipart/form-data). Runs nothing. The request states the organization its page shows in `x-obp-organization`.

Requires features: model.manage

Responses

201The draft
Content-Type: application/json
{}
400No organization, a request that does not state the organization its page shows, no file, a non-.xlsx upload, no target month, an unreadable file, or a layout refusal
Content-Type: application/json
{
  "error": "string"
}
409Another create won the race (its draft is in the body), or the page shows another organization than the one now selected (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
413Upload exceeds 25 MB
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/model/workbook/sessions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/sessions/{id}
Auth requiredmodel.view

One draft of this organization, whatever its status

Requires features: model.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The draft
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such draft in this organization, or a malformed id
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/sessions/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/model/workbook/sessions/{id}
Auth requiredmodel.manage

Discard an OPEN draft and its stored file

Requires features: model.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200Discarded
Content-Type: application/json
{
  "id": "string"
}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such draft in this organization, or a malformed id
Content-Type: application/json
{
  "error": "string"
}
409The draft has run; its file belongs to that run
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/model/workbook/sessions/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/model/workbook/sessions/{id}/inputs
Auth requiredmodel.view

The draft’s inputs board: every source it needs, its state, and why a run cannot start

Requires features: model.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The board (a `SourceBoard`, at the top level) with the `organizationId` it answers for
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such draft in this organization, or a malformed id
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/model/workbook/sessions/:id/inputs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Gusto

Showing 9 of 9 endpoints
PUT/gusto/department-rules
Auth requiredgusto.manage

Map ONE Gusto department title to a month-end profile department, with capitalize and ramp

The body is ONE rule: `{ "gustoDepartmentTitle": "…", "profileDepartmentName": "…" | null,` "capitalizePct": 0..1 | null, "ramp": "…" | null }`. The title is trimmed and `""` is the default row; the profile department is stored in the month-end profile’s own spelling, and a name the profile does not hold is refused 400 `department_not_in_profile`. A held department title that differs from this one only by case is refused 409 `department_title_conflict`. A list is refused: the CSV importer owns bulk changes. A change of an existing rule names the `updatedAt` it was read with in the `x-om-ext-optimistic-lock-expected-updated-at` header (never in the body): without it the answer is 428 (`optimistic_lock_required`), and against a stale one it is 409 (`optimistic_lock_conflict`). Send the organization the page shows in `x-obp-organization`. Requires `gusto.manage`. Requires features: gusto.manage

Request body (application/json)

{
  "gustoDepartmentTitle": "string",
  "capitalizePct": null
}

Responses

200The saved rule (canonical profile spelling) and its new `updatedAt`
Content-Type: application/json
{
  "rule": {
    "id": "string",
    "gustoDepartmentTitle": "string",
    "profileDepartmentName": null,
    "capitalizePct": null,
    "ramp": null,
    "updatedAt": "string"
  },
  "updatedAt": "string"
}
400No organization selected, none stated by the page (`organization_not_stated`), an invalid rule (`invalid_department_rule`), no month-end department profile / an empty one (`department_profile_missing`), or a profile name the month-end profile does not hold (`department_not_in_profile`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409Saved against a stale version (`optimistic_lock_conflict`), a held title differing only by case (`department_title_conflict`), or the selected organization is no longer the one the page shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
428An existing rule was saved without the `x-om-ext-optimistic-lock-expected-updated-at` version header (`optimistic_lock_required`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure, or a stored profile that could not be read back
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/gusto/department-rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"gustoDepartmentTitle\": \"string\",
  \"capitalizePct\": null
}"
DELETE/gusto/department-rules
Auth requiredgusto.manage

Forget the mapping for ONE Gusto department title, leaving every other rule as it is

The body is `{ "gustoDepartmentTitle": "…" }`, matched trimmed and case-folded. A row that is already gone answers 200 `{ "deleted": false }` when no version was named. A change of an existing rule names the `updatedAt` it was read with in the `x-om-ext-optimistic-lock-expected-updated-at` header (never in the body): without it the answer is 428 (`optimistic_lock_required`), and against a stale one it is 409 (`optimistic_lock_conflict`). Send the organization the page shows in `x-obp-organization`. Requires `gusto.manage`. Requires features: gusto.manage

Request body (application/json)

{
  "gustoDepartmentTitle": "string"
}

Responses

200Whether a rule was removed
Content-Type: application/json
{
  "deleted": true
}
400No organization selected, none stated by the page (`organization_not_stated`), a missing/blank title (`invalid_department_rule`), or no month-end department profile (`department_profile_missing`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409Deleted against a stale version, or a version named for a row that is gone (`optimistic_lock_conflict`), or the selected organization is no longer the one the page shows
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
428An existing rule was deleted without the `x-om-ext-optimistic-lock-expected-updated-at` version header (`optimistic_lock_required`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure, or a stored profile that could not be read back
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/gusto/department-rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"gustoDepartmentTitle\": \"string\"
}"
GET/gusto/oauth/callback

Gusto redirects here after the admin consents; the app exchanges the code and connects.

Responses

200Success response
Content-Type: application/json
"string"
302Redirect back to the integration page with a flash result
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/gusto/oauth/callback" \
  -H "Accept: application/json"
POST/gusto/oauth/initiate
Auth requiredgusto.connect

Discover the Gusto authorization server, register this deployment (DCR), mint a state nonce and return the authorize URL.

Requires features: gusto.connect

Responses

200Authorize URL returned
Content-Type: application/json
{
  "authorizeUrl": "string"
}
400No concrete organization selected, none stated in `x-obp-organization`, or "All organizations" selected
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409The stated organization is not the one now selected, or the credentials store is unavailable
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
502OAuth discovery or registration failed, or the authorization server is not the one this connection is pinned to
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/gusto/oauth/initiate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/gusto/people/{personUuid}/override
Auth requiredgusto.manage

Save a person’s full exception set, against the version it was read at

The body is one full replacement — `{ "kind": "employee" | "contractor", "departmentTitle": string | null, "capitalizePct": 0..1 | null, "ramp": string | null, "include": boolean }`; a partial body is refused, and `capitalizePct` is a FRACTION (the UI converts a percent). `departmentTitle` is the Gusto department TITLE the person is counted with, and must name a department this organization can resolve. A body that states no exception at all removes the row and answers `{ override: null, updatedAt: null }`. Send the version read in the `x-om-ext-optimistic-lock-expected-updated-at` header — never in the body — and the organization the page shows in `x-obp-organization`. Requires `gusto.manage` and a selected, mapped organization. Requires features: gusto.manage

Parameters

NameInRequiredSchemaDescription
personUuidpathYesany—
x-om-ext-optimistic-lock-expected-updated-atheaderNoany—
x-obp-organizationheaderNoany—

Request body (application/json)

{
  "kind": "employee",
  "departmentTitle": null,
  "capitalizePct": null,
  "ramp": null,
  "include": true
}

Responses

200The saved exception and its new `updatedAt`, or both null when the body reset the row
Content-Type: application/json
{
  "override": null,
  "updatedAt": null
}
400No organization selected (`organization_not_selected`), none stated by the page (`organization_not_stated`), a body the schema refuses (`invalid_override_body`), or a department no rule, department or synced person names (`department_unknown`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
404The uuid is not in the synced roster of the mapped company (`person_not_found`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409The version named in `x-om-ext-optimistic-lock-expected-updated-at` is stale, or names a row that has gone (`optimistic_lock_conflict`); or the stated organization is not the one now selected (`organization_changed`); or no Gusto company is mapped to this organization (`company_unmapped`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
428An existing row was edited or deleted without the version it was read at (`optimistic_lock_required`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/gusto/people/string/override" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"kind\": \"employee\",
  \"departmentTitle\": null,
  \"capitalizePct\": null,
  \"ramp\": null,
  \"include\": true
}"
DELETE/gusto/people/{personUuid}/override
Auth requiredgusto.manage

Reset a person to the department rules, against the version it was read at

The person uuid is the path parameter; there is no body. Send the version read in the `x-om-ext-optimistic-lock-expected-updated-at` header. A person with no exception is `{ "deleted": false }` when no version was sent, so a repeated reset is an answer rather than an error; a version naming a row that has gone is the gone-record 409. Send the organization the page shows in `x-obp-organization`. Requires `gusto.manage` and a selected organization; a mapped Gusto company is not required, so an override left behind by a moved company mapping can still be reset. Requires features: gusto.manage

Parameters

NameInRequiredSchemaDescription
personUuidpathYesany—
x-om-ext-optimistic-lock-expected-updated-atheaderNoany—
x-obp-organizationheaderNoany—

Responses

200Whether an exception row was removed
Content-Type: application/json
{
  "deleted": true
}
400No organization selected (`organization_not_selected`), or none stated by the page (`organization_not_stated`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409The version named in `x-om-ext-optimistic-lock-expected-updated-at` is stale, or names a row that has gone (`optimistic_lock_conflict`); or the stated organization is not the one now selected (`organization_changed`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
428An existing row was edited or deleted without the version it was read at (`optimistic_lock_required`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/gusto/people/string/override" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/gusto/roster
Auth requiredgusto.view

Read the synced Gusto people with the client’s headcount rules applied

The one roster read consumed by BOTH the Headcount page (which falls back to its reference data when `live` is false) and the Gusto People page (which also renders the department mapping). It serves them when a Gusto company is CONNECTED and has synced rows for the company this organization is MAPPED to (`live: true`); otherwise it answers 200 with an empty roster and `live: false`. `connected` says a connection row exists and `connectionStatus` says what it is doing (`pending` / `connected` / `error`), so a consent still in flight or a failed connect is not read as "no Gusto at all". `profileDepartments` is the organization’s month-end department profile and `profileStatus` (`ready` / `missing` / `unreadable`) says whether it could be read; a missing or unreadable profile is a state, not an error. `departments` is the four-source mapping union — the synced directory, titles carried by people or person anchors, rule titles, and the always-present default row — with its `rule`, `presentIn`, `personCount` and `duplicateCase`. Every person names the Gusto `departmentTitle`, any `departmentTitleOverride` anchor, the `effectiveDepartmentTitle` the rules resolved, the month-end `department` (canonicalized to the profile’s spelling) and the `departmentSource` (`person` / `department` / `default` / `none`) that supplied it. Requires `gusto.view` and a concrete organization selected. Names are read decrypted through the framework’s encryption layer, scoped to the caller’s organization and tenant; the roster rows are scoped further, to the mapped company. Requires features: gusto.view

Responses

200The roster: people (employees and contractors), the rules and overrides, the department union, and every problem found
Content-Type: application/json
{
  "live": true,
  "connected": true,
  "connectionStatus": null,
  "companyName": null,
  "companyUuid": null,
  "asOf": null,
  "people": [
    {
      "personUuid": "string",
      "kind": "employee",
      "name": "string",
      "title": null,
      "departmentTitle": null,
      "departmentTitleOverride": null,
      "effectiveDepartmentTitle": null,
      "department": null,
      "departmentSource": "person"
    }
  ],
  "problems": [
    {}
  ],
  "rules": [
    {}
  ],
  "overrides": [
    {}
  ],
  "profileDepartments": [
    "string"
  ],
  "profileStatus": "ready",
  "departments": [
    {
      "gustoDepartmentTitle": "string",
      "gustoUuid": null,
      "presentIn": [
        "directory"
      ],
      "rule": null,
      "personCount": 1,
      "isDefault": true,
      "duplicateCase": true
    }
  ]
}
400No concrete organization is selected
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409The request states an organization other than the selected one
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500The roster could not be read; the page falls back to its reference data
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/gusto/roster" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/gusto/spike
Auth requiredgusto.manage

Record the live MCP catalogue and one read-only call per battery step for the connected company

Runs a fixed read-only battery (17 steps after `tools/list`, one of which may be asked of up to three employees) against the Gusto MCP server through the organization’s own stored grant, for the company that grant reaches, and returns the raw report: what the server said of itself in the handshake, the catalogue exactly as published, the health probe, the company the grant named and whether the organization is mapped to it, the ids carried between calls and where each was read from, and one record per call. A step is asked under the first of its tool’s names the catalogue lists, with arguments named by that tool’s own input schema; its record holds the name used, what the call was about, the arguments sent, timing, error, pagination signals and a truncated raw sample, or says why the step was not attempted. Two steps repeat the employees list with `per: 1` (pages 1 and 2) to show whether the server pages, and one repeats it with `include: all_compensations` to show whether the rate history comes with the roster. A date range a tool requires is given the trailing twelve months, and so is the one the contractor-payments tool declares. Nothing is written and no `data_sync` run is started. The report carries the client’s own data as the server returned it; a credential in a tool’s answer is redacted, and the report never carries a token. Requires `gusto.manage`, a concrete organization selected, and a Gusto connection for that organization. A company mapping is not required: when there is none, the battery takes the company from the grant’s own answers. Requires features: gusto.manage

Responses

200The report, as the browser hands it back for download
Content-Type: application/json
{
  "generatedAt": "string",
  "sdkVersion": "string",
  "orgScoped": true,
  "server": null,
  "company": null,
  "grantCompany": null,
  "carriedIds": {
    "key": null
  },
  "probe": {},
  "tools": [
    {}
  ],
  "calls": [
    {}
  ],
  "notes": [
    "string"
  ]
}
400No concrete organization is selected
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
409Gusto is not connected for this organization (or the connection needs reconnecting), or the stated organization is not the selected one
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure while running the battery
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/gusto/spike" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/gusto/sync
Auth requiredgusto.manage

Start one data_sync run for the Gusto company mapped to the selected organization.

Requires features: gusto.manage

Responses

200Run started
Content-Type: application/json
{
  "enqueued": 1,
  "company": "string",
  "runIds": [
    "string"
  ]
}
400No concrete organization selected, none stated in `x-obp-organization`, or no company mapped to it
Content-Type: application/json
{
  "error": "string"
}
409The stated organization is not the one now selected (the page was loaded for another client)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure while starting the run
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/gusto/sync" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Mail

Showing 8 of 8 endpoints
GET/mail/connection
Auth requiredmail.view

Every mailbox this tenant has connected, and each one's current state

Requires features: mail.view

Responses

200{ connections: [{ connectionId, provider, mailboxAddress, mailboxDisplayName, status, lastError, connectedAt, lastPolledAt, organizationId, organizationName, inbound, disconnectable, retainsUnconfirmedGrant }] } — one entry per connection that has a mailbox address and a live client company; rows kept only as connect anchors are omitted, so an empty array means nothing is connected rather than that no row exists
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/mail/connection" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/mail/connection/forget
Auth requiredmail.connect

Clear the recorded address of a disconnected mailbox (local record only — revokes nothing)

Requires features: mail.connect

Responses

200{ forgotten: true }
Content-Type: application/json
"string"
400No connectionId given
Content-Type: application/json
"string"
409The connection is not disconnected, so there is nothing to forget yet
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/mail/connection/forget" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/mail/inbound

POST /mail/inbound

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/mail/inbound" \
  -H "Accept: application/json"
GET/mail/oauth/callback

GET /mail/oauth/callback

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/mail/oauth/callback" \
  -H "Accept: application/json"
POST/mail/oauth/disconnect
Auth requiredmail.connect

Disconnect ONE mailbox, named by connectionId (revoke where the vendor allows it, stop polling, then forget it here)

Requires features: mail.connect

Responses

200{ disconnected: true, revoke } — revoke is revoked | shared | already_ended | nothing_to_revoke | failed | unsupported
Content-Type: application/json
"string"
400No connectionId given
Content-Type: application/json
"string"
500Could not disconnect
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/mail/oauth/disconnect" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/mail/oauth/initiate
Auth requiredmail.connect

POST /mail/oauth/initiate

Requires features: mail.connect

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/mail/oauth/initiate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/mail/question-scope
Auth requiredmail.view

GET /mail/question-scope

Requires features: mail.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/mail/question-scope" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/mail/question-scope
Auth requiredmail.connect

PUT /mail/question-scope

Requires features: mail.connect

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/mail/question-scope" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Drive

Showing 8 of 8 endpoints
GET/drive/connection
Auth requireddrive.view

Every Google Drive account this tenant has connected, and each one's current state

Requires features: drive.view

Responses

200{ connections: [{ connectionId, provider, accountEmail, accountDisplayName, status, lastError, connectedAt, organizationId, organizations, disconnectable, retainsUnconfirmedGrant }] } — one entry per connected Google account whose client company is live; rows kept only as connect anchors (forgotten, never completed, or belonging to a deleted company) are omitted, so an empty array means nothing is connected rather than that no row exists
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/drive/connection" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/drive/connection/forget
Auth requireddrive.connect

Clear the recorded address of a disconnected Drive account (local record only — revokes nothing)

Requires features: drive.connect

Responses

200{ forgotten: true }
Content-Type: application/json
"string"
400No connectionId given
Content-Type: application/json
"string"
409The connection is not disconnected, so there is nothing to forget yet
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/drive/connection/forget" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/drive/oauth/callback

GET /drive/oauth/callback

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/drive/oauth/callback" \
  -H "Accept: application/json"
POST/drive/oauth/disconnect
Auth requireddrive.connect

Disconnect ONE connected Google Drive account, named by connectionId (revoke at Google unless Gmail shares the grant, then forget it here)

Requires features: drive.connect

Responses

200{ disconnected: true, revoke } — revoke is revoked | shared | already_ended | nothing_to_revoke | failed
Content-Type: application/json
"string"
400No connectionId given
Content-Type: application/json
"string"
500Could not disconnect
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/drive/oauth/disconnect" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/drive/oauth/initiate
Auth requireddrive.connect

POST /drive/oauth/initiate

Requires features: drive.connect

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/drive/oauth/initiate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/drive/scopes
Auth requireddrive.view

GET /drive/scopes

Requires features: drive.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/drive/scopes" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/drive/scopes
Auth requireddrive.connect

PUT /drive/scopes

Requires features: drive.connect

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/drive/scopes" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/drive/targets
Auth requireddrive.view

GET /drive/targets

Requires features: drive.view

Responses

200Success response
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/drive/targets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Obligations

Showing 17 of 17 endpoints
GET/obligations/checklist
Auth requiredobligations.view

Completeness of one client: present, missing (named) and unverified required slots

Requires features: obligations.view

Responses

200Checklist state and its per-requirement items
Content-Type: application/json
{
  "state": "complete",
  "items": [
    {}
  ],
  "present": [
    {}
  ],
  "missing": [
    {}
  ],
  "unverified": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/checklist" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/proposals
Auth requiredobligations.view

Proposals (including those needing attention), scoped to the selected company

Requires features: obligations.view

Responses

200Proposals
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/proposals" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/obligations/proposals
Auth requiredobligations.manage

Create a proposal from an uploaded document (never a register row)

Requires features: obligations.manage

Request body (application/json)

{}

Responses

200The proposal id and its status
Content-Type: application/json
{
  "proposalId": "string",
  "status": "string"
}
400No file, a bad kind, or no organization selected
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/obligations/proposals" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
GET/obligations/proposals/{id}
Auth requiredobligations.view

Read one proposal, scoped to the selected company

Requires features: obligations.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The proposal
Content-Type: application/json
{
  "item": {}
}
404No such proposal
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/proposals/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PATCH/obligations/proposals/{id}
Auth requiredobligations.manage

Persist corrections to a proposal, or reclassify it to another register kind

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Request body (application/json)

{
  "fields": {
    "key": {
      "value": "string"
    }
  }
}

Responses

200The updated proposal
Content-Type: application/json
{
  "item": {}
}
409The proposal was already confirmed or discarded
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/obligations/proposals/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"fields\": {
    \"key\": {
      \"value\": \"string\"
    }
  }
}"
POST/obligations/proposals/{id}/confirm
Auth requiredobligations.manage

Create the register row from the proposal, once (idempotent)

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The confirmed row id (and whether it was newly created)
Content-Type: application/json
{
  "confirmed": true,
  "created": true,
  "recordId": null
}
404No such proposal
Content-Type: application/json
{
  "error": "string"
}
409The proposal needs attention first
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/obligations/proposals/:id/confirm" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/obligations/proposals/{id}/discard
Auth requiredobligations.manage

Mark the proposal discarded; the stored document remains for audit

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The discarded proposal id and status
Content-Type: application/json
{
  "id": "string",
  "status": "string"
}
404No such proposal
Content-Type: application/json
{
  "error": "string"
}
409The proposal was already confirmed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/obligations/proposals/:id/discard" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/records/{id}
Auth requiredobligations.view

The register row (or its still-unconfirmed proposal), document of record, and linked records

Requires features: obligations.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The detail payload
Content-Type: application/json
{
  "id": "string",
  "kind": null
}
404No such record or proposal
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/records/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PATCH/obligations/records/{id}
Auth requiredobligations.manage

Edit a confirmed row — requires the optimistic-lock version header

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Request body (application/json)

{
  "fields": {}
}

Responses

200The updated row
Content-Type: application/json
{
  "record": {}
}
409A concurrent edit already changed the row
Content-Type: application/json
{
  "code": "string"
}
428No version header was sent
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/obligations/records/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"fields\": {}
}"
GET/obligations/registers/contracts
Auth requiredobligations.view

Vendor contracts for the selected company, with term end and notice window

Requires features: obligations.view

Responses

200Contracts
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/registers/contracts" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/registers/insurance
Auth requiredobligations.view

Policies and COIs for the selected company, as separate arrays (never merged)

Requires features: obligations.view

Responses

200Policies and certificates
Content-Type: application/json
{
  "policies": [
    {}
  ],
  "certificates": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/registers/insurance" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/registers/leases
Auth requiredobligations.view

Leases for the selected company, including their term type

Requires features: obligations.view

Responses

200Leases
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/registers/leases" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/renewals
Auth requiredobligations.view

Raw renewal candidates (policy/COI expiry, lease end, contract notice deadline) for all visible clients

Requires features: obligations.view

Responses

200Renewal candidates
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/renewals" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/obligations/requirements
Auth requiredobligations.view

List the requirements a client checklist is evaluated against

Requires features: obligations.view

Responses

200Requirements
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/obligations/requirements" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/obligations/requirements
Auth requiredobligations.manage

Define one required slot (insurance | lease | contract) for a client

Requires features: obligations.manage

Request body (application/json)

{
  "kind": "insurance",
  "label": null
}

Responses

200The created requirement
Content-Type: application/json
{
  "requirement": {}
}
400An unknown kind, a bad count, or no organization selected
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/obligations/requirements" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"kind\": \"insurance\",
  \"label\": null
}"
PATCH/obligations/requirements/{id}
Auth requiredobligations.manage

Edit a requirement — requires the optimistic-lock version header

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Request body (application/json)

{
  "label": null
}

Responses

200The updated requirement
Content-Type: application/json
{
  "requirement": {}
}
400An unknown kind or a bad count
Content-Type: application/json
{
  "error": "string"
}
404No such requirement
Content-Type: application/json
{
  "error": "string"
}
409A concurrent edit already changed the requirement
Content-Type: application/json
{
  "code": "string"
}
428No version header was sent
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/obligations/requirements/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"label\": null
}"
DELETE/obligations/requirements/{id}
Auth requiredobligations.manage

Soft-delete a requirement — requires the optimistic-lock version header

Requires features: obligations.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The requirement was removed
Content-Type: application/json
{
  "deleted": true,
  "id": "string"
}
404No such requirement
Content-Type: application/json
{
  "error": "string"
}
409A concurrent edit already changed the requirement
Content-Type: application/json
{
  "code": "string"
}
428No version header was sent
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/obligations/requirements/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Advisory

Showing 5 of 5 endpoints
GET/advisory/documents
Auth requiredadvisory.view

Per-document ingest outcome, including which pages had no text layer and need OCR

Requires features: advisory.view

Responses

200Documents
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/advisory/documents" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/advisory/draft
Auth requiredadvisory.review

Runs classify -> evidence -> draft inline and stores the result. Omit questionId to draft all pending questions.

Requires features: advisory.review

Responses

200Outcomes
Content-Type: application/json
{
  "ok": true,
  "outcomes": [
    {}
  ]
}
503Document search is not configured
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/advisory/draft" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/advisory/questions
Auth requiredadvisory.view

Questions from every connected source with their latest cited draft reply, scoped to the selected company plus anything still unassigned

Requires features: advisory.view

Responses

200Inbox rows
Content-Type: application/json
{
  "items": [
    {}
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/advisory/questions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/advisory/questions
Auth requiredadvisory.review

Advisory review inbox

Requires features: advisory.review

Responses

201Success response
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/advisory/questions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/advisory/review
Auth requiredadvisory.review

Approve, save edits to, or dismiss a drafted reply. Records the reviewer and their text; sends nothing anywhere.

Requires features: advisory.review

Responses

200Updated
Content-Type: application/json
{
  "ok": true,
  "status": "string"
}
409No draft to review yet
Content-Type: application/json
{
  "error": "string"
}
422Refused — an approved reply must cite the document or figure it came from
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/advisory/review" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Auth

Showing 1 of 1 endpoints
GET/auth/users/consents
Auth requiredauth.users.edit

List user consents

Returns all consent records for a given user, with integrity verification status. Requires features: auth.users.edit

Parameters

NameInRequiredSchemaDescription
userIdqueryYesany—

Responses

200Consent list returned
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/auth/users/consents?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Configs

Showing 8 of 8 endpoints
GET/configs/cache
Auth requiredconfigs.cache.view

Get cache statistics

Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available. Requires features: configs.cache.view

Responses

200Cache statistics
Content-Type: application/json
{
  "generatedAt": "string",
  "totalKeys": 1,
  "segments": [
    {
      "segment": "string",
      "resource": null,
      "method": null,
      "path": null,
      "keyCount": 1,
      "keys": [
        "string"
      ]
    }
  ]
}
500Failed to resolve cache stats
Content-Type: application/json
{
  "error": "string"
}
503Cache service unavailable
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/configs/cache
Auth requiredconfigs.cache.manage

Purge cache

Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge. Requires features: configs.cache.manage

Request body (application/json)

{
  "action": "purgeAll"
}

Responses

200Cache segment cleared successfully
Content-Type: application/json
{
  "action": "purgeSegment",
  "segment": "string",
  "deleted": 1,
  "stats": {
    "generatedAt": "string",
    "totalKeys": 1,
    "segments": [
      {
        "segment": "string",
        "resource": null,
        "method": null,
        "path": null,
        "keyCount": 1,
        "keys": [
          "string"
        ]
      }
    ]
  }
}
400Invalid request - missing segment identifier for purgeSegment action
Content-Type: application/json
{
  "error": "string"
}
500Failed to purge cache
Content-Type: application/json
{
  "error": "string"
}
503Cache service unavailable
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"purgeAll\"
}"
GET/configs/module-telemetry
Auth requiredconfigs.system_status.view

Get module resource usage telemetry

Returns in-process module resource attribution for API routes, event subscribers, and queue workers. Requires features: configs.system_status.view

Responses

200Module resource usage report
Content-Type: application/json
{
  "generatedAt": "string",
  "startedAt": "string",
  "enabled": true,
  "bucketIntervalMs": 1,
  "totals": {
    "modules": 1,
    "operations": 1,
    "calls": 1,
    "errors": 1,
    "totalDurationMs": 1,
    "totalCpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1
  },
  "thresholds": {
    "p95DurationMs": 1,
    "cpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1,
    "errors": 1
  },
  "modules": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "candidates": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "buckets": [
    {
      "bucketStart": "string",
      "bucketEnd": "string",
      "bucketIntervalMs": 1,
      "stage": "startup",
      "partial": true,
      "totals": {
        "modules": 1,
        "calls": 1,
        "errors": 1,
        "totalDurationMs": 1,
        "totalCpuMs": 1,
        "positiveHeapDeltaBytes": 1,
        "positiveRssDeltaBytes": 1
      },
      "modules": [
        {
          "moduleId": "string",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "surfaces": [
            {
              "surface": "api",
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuMs": 1,
              "positiveHeapDeltaBytes": 1,
              "positiveRssDeltaBytes": 1
            }
          ],
          "topOperations": [
            {
              "moduleId": "string",
              "surface": "api",
              "operation": "string",
              "resourceId": null,
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "maxDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuUserMs": 1,
              "totalCpuSystemMs": 1,
              "maxCpuMs": 1,
              "totalHeapDeltaBytes": 1,
              "positiveHeapDeltaBytes": 1,
              "maxHeapDeltaBytes": 1,
              "totalRssDeltaBytes": 1,
              "positiveRssDeltaBytes": 1,
              "maxRssDeltaBytes": 1,
              "firstSeenAt": "string",
              "lastSeenAt": "string"
            }
          ],
          "candidateReasons": [
            "string"
          ]
        }
      ]
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/configs/module-telemetry
Auth requiredconfigs.manage

Clear module telemetry data

Development-only endpoint that clears in-memory module telemetry and local process telemetry files. Requires features: configs.manage

Responses

200Module telemetry cleared
Content-Type: application/json
{
  "cleared": true
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/configs/system-status
Auth requiredconfigs.system_status.view

Get system health status

Returns comprehensive system health information including environment details, version, resource usage, and service connectivity status. Requires features: configs.system_status.view

Responses

200System status snapshot
Content-Type: application/json
{
  "generatedAt": "string",
  "runtimeMode": "development",
  "categories": [
    {
      "key": "profiling",
      "labelKey": "string",
      "descriptionKey": null,
      "items": [
        {
          "key": "string",
          "category": "profiling",
          "kind": "boolean",
          "labelKey": "string",
          "descriptionKey": "string",
          "docUrl": null,
          "defaultValue": null,
          "state": "enabled",
          "value": null,
          "normalizedValue": null
        }
      ]
    }
  ]
}
500Failed to load system status
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/configs/system-status
Auth requiredconfigs.manage

Clear system cache

Purges the entire cache for the current tenant. Useful for troubleshooting or forcing fresh data loading. Requires features: configs.manage

Responses

200Cache cleared successfully
Content-Type: application/json
{
  "cleared": true
}
500Failed to purge cache
Content-Type: application/json
{
  "error": "string"
}
503Cache service unavailable
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/configs/upgrade-actions
Auth requiredconfigs.manage

List pending upgrade actions

Returns a list of pending upgrade actions for the current version. These are one-time setup tasks that need to be executed after upgrading to a new version. Requires organization and tenant context. Requires features: configs.manage

Responses

200List of pending upgrade actions
Content-Type: application/json
{
  "version": "string",
  "actions": [
    {
      "id": "string",
      "version": "string",
      "message": "string",
      "ctaLabel": "string",
      "successMessage": "string",
      "loadingLabel": "string"
    }
  ]
}
400Missing organization or tenant context
Content-Type: application/json
{
  "error": "string"
}
500Failed to load upgrade actions
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/configs/upgrade-actions
Auth requiredconfigs.manage

Execute upgrade action

Executes a specific upgrade action by ID. Typically used for one-time setup tasks like seeding example data after version upgrade. Returns execution status and localized success message. Requires features: configs.manage

Request body (application/json)

{
  "actionId": "string"
}

Responses

200Upgrade action executed successfully
Content-Type: application/json
{
  "status": "string",
  "message": "string",
  "version": "string"
}
400Invalid request body or missing context
Content-Type: application/json
{
  "error": "string"
}
500Failed to execute upgrade action
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"actionId\": \"string\"
}"

Dashboards

Showing 10 of 10 endpoints
GET/dashboards/layout
Auth requireddashboards.view

Load the current dashboard layout

Returns the saved widget layout together with the widgets the current user is allowed to place. Requires features: dashboards.view

Responses

200Current dashboard layout and available widgets.
Content-Type: application/json
{
  "layout": {
    "items": [
      {
        "id": "00000000-0000-4000-8000-000000000000",
        "widgetId": "string",
        "order": 1
      }
    ]
  },
  "allowedWidgetIds": [
    "string"
  ],
  "canConfigure": true,
  "context": {
    "userId": "00000000-0000-4000-8000-000000000000",
    "tenantId": null,
    "organizationId": null,
    "userName": null,
    "userEmail": null,
    "userLabel": "string"
  },
  "widgets": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/dashboards/layout
Auth requireddashboards.configure

Persist dashboard layout changes

Saves the provided widget ordering, sizes, and settings for the current user. Requires features: dashboards.configure

Request body (application/json)

{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "widgetId": "string",
      "order": 1
    }
  ]
}

Responses

200Layout updated successfully.
Content-Type: application/json
{
  "ok": true
}
400Invalid layout payload
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"items\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"widgetId\": \"string\",
      \"order\": 1
    }
  ]
}"
PATCH/dashboards/layout/{itemId}
Auth requireddashboards.configure

Update a dashboard layout item

Adjusts the size or settings for a single widget within the dashboard layout. Requires features: dashboards.configure

Parameters

NameInRequiredSchemaDescription
itemIdpathYesany—

Request body (application/json)

{}

Responses

200Layout item updated.
Content-Type: application/json
{
  "ok": true
}
400Invalid payload or missing item id
Content-Type: application/json
{
  "error": "string"
}
404Item not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/dashboards/layout/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
GET/dashboards/roles/widgets
Auth requireddashboards.admin.assign-widgets

Fetch widget assignments for a role

Returns the widgets explicitly assigned to the given role together with the evaluation scope. Requires features: dashboards.admin.assign-widgets

Parameters

NameInRequiredSchemaDescription
roleIdqueryYesany—
tenantIdqueryNoany—
organizationIdqueryNoany—

Responses

200Current widget configuration for the role.
Content-Type: application/json
{
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
400Missing role identifier
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dashboards/roles/widgets?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/dashboards/roles/widgets
Auth requireddashboards.admin.assign-widgets

Update widgets assigned to a role

Persists the widget list for a role within the provided tenant and organization scope. Requires features: dashboards.admin.assign-widgets

Request body (application/json)

{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "widgetIds": [
    "string"
  ]
}

Responses

200Widgets updated successfully.
Content-Type: application/json
{
  "ok": true,
  "widgetIds": [
    "string"
  ]
}
400Invalid payload or unknown widgets
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/dashboards/roles/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"widgetIds\": [
    \"string\"
  ]
}"
GET/dashboards/users/widgets
Auth requireddashboards.admin.assign-widgets

Read widget overrides for a user

Returns the widgets inherited and explicitly configured for the requested user within the current scope. Requires features: dashboards.admin.assign-widgets

Parameters

NameInRequiredSchemaDescription
userIdqueryYesany—
tenantIdqueryNoany—
organizationIdqueryNoany—

Responses

200Widget settings for the user.
Content-Type: application/json
{
  "mode": "inherit",
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "effectiveWidgetIds": [
    "string"
  ],
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
400Missing user identifier
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dashboards/users/widgets?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/dashboards/users/widgets
Auth requireddashboards.admin.assign-widgets

Update user-specific dashboard widgets

Sets the widget override mode and allowed widgets for a user. Passing `mode: inherit` clears overrides. Requires features: dashboards.admin.assign-widgets

Request body (application/json)

{
  "userId": "00000000-0000-4000-8000-000000000000",
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}

Responses

200Overrides saved.
Content-Type: application/json
{
  "ok": true,
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}
400Invalid payload or unknown widgets
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/dashboards/users/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"mode\": \"inherit\",
  \"widgetIds\": [
    \"string\"
  ]
}"
GET/dashboards/widgets/catalog
Auth requireddashboards.admin.assign-widgets

List available dashboard widgets

Returns the catalog of widgets that modules expose, including defaults and feature requirements. Requires features: dashboards.admin.assign-widgets

Responses

200Widgets available for assignment.
Content-Type: application/json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dashboards/widgets/catalog" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/dashboards/widgets/data
Auth requiredanalytics.view

Fetch aggregated data for dashboard widgets

Executes an aggregation query against the specified entity type and returns the result. Supports date range filtering, grouping, and period-over-period comparison. Requires features: analytics.view

Request body (application/json)

{
  "entityType": "string",
  "metric": {
    "field": "string",
    "aggregate": "count"
  }
}

Responses

200Aggregated data for the widget.
Content-Type: application/json
{
  "value": null,
  "data": [
    {
      "value": null
    }
  ],
  "metadata": {
    "fetchedAt": "string",
    "recordCount": 1
  }
}
400Invalid request payload
Content-Type: application/json
{
  "error": "string"
}
500Internal server error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dashboards/widgets/data" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\",
  \"metric\": {
    \"field\": \"string\",
    \"aggregate\": \"count\"
  }
}"
POST/dashboards/widgets/data/batch
Auth requiredanalytics.view

Fetch aggregated data for multiple dashboard widgets in one request

Resolves a batch of widget data requests with a single authentication, RBAC, organization-scope, and database-context setup. Each request is keyed by an opaque widget id and resolved independently, so a failure in one widget does not fail the batch. Requires features: analytics.view

Request body (application/json)

{
  "requests": [
    {
      "id": "string",
      "request": {
        "entityType": "string",
        "metric": {
          "field": "string",
          "aggregate": "count"
        }
      }
    }
  ]
}

Responses

200Per-widget aggregation results keyed by request id.
Content-Type: application/json
{
  "results": [
    {
      "id": "string",
      "ok": true,
      "data": {
        "value": null,
        "data": [
          {
            "value": null
          }
        ],
        "metadata": {
          "fetchedAt": "string",
          "recordCount": 1
        }
      }
    }
  ]
}
400Invalid request payload
Content-Type: application/json
{
  "error": "string"
}
500Internal server error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dashboards/widgets/data/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"requests\": [
    {
      \"id\": \"string\",
      \"request\": {
        \"entityType\": \"string\",
        \"metric\": {
          \"field\": \"string\",
          \"aggregate\": \"count\"
        }
      }
    }
  ]
}"

Dictionaries

Showing 11 of 11 endpoints
GET/dictionaries
Auth requireddictionaries.view

List dictionaries

Returns dictionaries accessible to the current organization, optionally including inactive records. Requires features: dictionaries.view

Parameters

NameInRequiredSchemaDescription
includeInactivequeryNoany—

Responses

200Dictionary collection.
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "name": "string",
      "description": null,
      "isSystem": true,
      "isActive": true,
      "managerVisibility": null,
      "organizationId": null,
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
500Failed to load dictionaries
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/dictionaries
Auth requireddictionaries.manage

Create dictionary

Registers a dictionary scoped to the current organization. Requires features: dictionaries.manage

Request body (application/json)

{
  "key": "string",
  "name": "string"
}

Responses

201Dictionary created.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
409Dictionary key already exists
Content-Type: application/json
{
  "error": "string"
}
500Failed to create dictionary
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"key\": \"string\",
  \"name\": \"string\"
}"
GET/dictionaries/{dictionaryId}
Auth requireddictionaries.view

Get dictionary

Returns details for the specified dictionary, including inheritance flags. Requires features: dictionaries.view

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Responses

200Dictionary details.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
400Invalid parameters
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to load dictionary
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PATCH/dictionaries/{dictionaryId}
Auth requireddictionaries.manage

Update dictionary

Updates mutable attributes of the dictionary. Currency dictionaries are protected from modification. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Request body (application/json)

{}

Responses

200Dictionary updated.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
400Validation failed or protected dictionary
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
409Dictionary key already exists
Content-Type: application/json
{
  "error": "string"
}
500Failed to update dictionary
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
DELETE/dictionaries/{dictionaryId}
Auth requireddictionaries.manage

Delete dictionary

Soft deletes the dictionary unless it is the protected currency dictionary. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Responses

200Dictionary archived.
Content-Type: application/json
{
  "ok": true
}
400Protected dictionary cannot be deleted
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to delete dictionary
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/dictionaries/{dictionaryId}/entries
Auth requireddictionaries.view

List dictionary entries

Returns entries for the specified dictionary ordered by its configured entry sort mode. Requires features: dictionaries.view

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Responses

200Dictionary entries.
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "value": "string",
      "label": "string",
      "color": null,
      "icon": null,
      "position": 1,
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
400Invalid parameters
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to load dictionary entries
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/dictionaries/{dictionaryId}/entries
Auth requireddictionaries.manage

Create dictionary entry

Creates a new entry in the specified dictionary. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Request body (application/json)

{
  "value": "string",
  "color": null,
  "icon": null
}

Responses

201Dictionary entry created.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to create dictionary entry
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"value\": \"string\",
  \"color\": null,
  \"icon\": null
}"
PATCH/dictionaries/{dictionaryId}/entries/{entryId}
Auth requireddictionaries.manage

Update dictionary entry

Updates the specified dictionary entry using the command bus pipeline. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—
entryIdpathYesany—

Request body (application/json)

{
  "color": null,
  "icon": null
}

Responses

200Dictionary entry updated.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Dictionary or entry not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to update entry
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PATCH "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"color\": null,
  \"icon\": null
}"
DELETE/dictionaries/{dictionaryId}/entries/{entryId}
Auth requireddictionaries.manage

Delete dictionary entry

Deletes the specified dictionary entry via the command bus. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—
entryIdpathYesany—

Responses

200Entry deleted.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Dictionary or entry not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to delete entry
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/dictionaries/{dictionaryId}/entries/reorder
Auth requireddictionaries.manage

Reorder dictionary entries

Updates the position of dictionary entries for drag-and-drop reordering. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Request body (application/json)

{
  "entries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "position": 1
    }
  ]
}

Responses

200Entries reordered.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Dictionary not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to reorder entries
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/reorder" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entries\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"position\": 1
    }
  ]
}"
POST/dictionaries/{dictionaryId}/entries/set-default
Auth requireddictionaries.manage

Set default dictionary entry

Marks the specified entry as the default for this dictionary, clearing any previous default. Requires features: dictionaries.manage

Parameters

NameInRequiredSchemaDescription
dictionaryIdpathYesany—

Request body (application/json)

{
  "entryId": "00000000-0000-4000-8000-000000000000"
}

Responses

200Default entry set.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}
404Dictionary or entry not found
Content-Type: application/json
{
  "error": "string"
}
500Failed to set default entry
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/set-default" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entryId\": \"00000000-0000-4000-8000-000000000000\"
}"

Directory

Showing 11 of 11 endpoints
GET/directory/organization-branding
Auth requireddirectory.organizations.view

Read sidebar branding for the selected organization

Returns the logo URL used by the backend sidebar for the currently selected organization. Requires features: directory.organizations.view

Responses

200Organization branding
Content-Type: application/json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "updatedAt": null
}
400A concrete organization scope is required
Content-Type: application/json
{
  "error": "string"
}
404Organization not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/directory/organization-branding
Auth requireddirectory.organizations.manage

Update sidebar branding for the selected organization

Stores an external image URL or an internal attachment image URL as the selected organization logo. Requires features: directory.organizations.manage

Request body (application/json)

{
  "logoUrl": null
}

Responses

200Updated organization branding
Content-Type: application/json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "updatedAt": null
}
400Save failed
Content-Type: application/json
{
  "error": "string"
}
409Organization branding changed since it was loaded
Content-Type: application/json
{
  "error": "string"
}
422Invalid logo URL
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logoUrl\": null
}"
GET/directory/organization-switcher
Auth required

Load organization switcher menu

Returns the hierarchical menu of organizations the current user may switch to within the active tenant.

Responses

200Organization switcher payload.
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "depth": 1,
      "selectable": true,
      "children": []
    }
  ],
  "selectedId": null,
  "canManage": true,
  "canViewAllOrganizations": true,
  "tenantId": null,
  "tenants": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true
    }
  ],
  "isSuperAdmin": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/organization-switcher" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/directory/organizations
Auth requireddirectory.organizations.view

List organizations

Returns organizations using options, tree, or paginated manage view depending on the `view` parameter. Requires features: directory.organizations.view

Parameters

NameInRequiredSchemaDescription
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—
viewqueryNoany—
idsqueryNoany—
tenantIdqueryNoany—
includeInactivequeryNoany—
statusqueryNoany—

Responses

200Organization data for the requested view.
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "parentId": null,
      "parentName": null,
      "tenantId": null,
      "tenantName": null,
      "rootId": null,
      "treePath": null
    }
  ]
}
400Invalid query or tenant scope
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/organizations?page=1&pageSize=50&view=options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/directory/organizations
Auth requireddirectory.organizations.manage

Create organization

Creates a new organization within a tenant and optionally assigns hierarchy relationships. Requires features: directory.organizations.manage

Request body (application/json)

{
  "name": "string",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}

Responses

201Organization created.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
PUT/directory/organizations
Auth requireddirectory.organizations.manage

Update organization

Updates organization details and hierarchy assignments. Requires features: directory.organizations.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}

Responses

200Organization updated.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
DELETE/directory/organizations
Auth requireddirectory.organizations.manage

Delete organization

Soft deletes an organization identified by id. Requires features: directory.organizations.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Organization deleted.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
GET/directory/tenants
Auth requireddirectory.tenants.view

List tenants

Returns tenants visible to the current user with optional search and pagination. Requires features: directory.tenants.view

Parameters

NameInRequiredSchemaDescription
idqueryNoany—
pagequeryNoany—
pageSizequeryNoany—
searchqueryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
isActivequeryNoany—

Responses

200Paged list of tenants.
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
400Invalid query parameters
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/directory/tenants?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/directory/tenants
Auth requireddirectory.tenants.manage

Create tenant

Creates a new tenant and returns its identifier. Requires features: directory.tenants.manage

Request body (application/json)

{
  "name": "string"
}

Responses

201Tenant created.
Content-Type: application/json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
PUT/directory/tenants
Auth requireddirectory.tenants.manage

Update tenant

Updates tenant properties such as name or activation state. Requires features: directory.tenants.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Tenant updated.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
DELETE/directory/tenants
Auth requireddirectory.tenants.manage

Delete tenant

Soft deletes the tenant identified by id. Requires features: directory.tenants.manage

Request body (application/json)

{
  "id": "00000000-0000-4000-8000-000000000000"
}

Responses

200Tenant removed.
Content-Type: application/json
{
  "ok": true
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"

Entities

Showing 19 of 19 endpoints
GET/entities/definitions
Auth required

List active custom field definitions

Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.

Parameters

NameInRequiredSchemaDescription
entityIdqueryNoany—
entityIdsqueryNoany—
fieldsetqueryNoany—

Responses

200Definition list
Content-Type: application/json
{
  "items": [
    {
      "key": "string",
      "kind": "string",
      "label": "string",
      "entityId": "string"
    }
  ]
}
400Missing entity id
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/entities/definitions
Auth requiredentities.definitions.manage

Upsert custom field definition

Creates or updates a custom field definition for the current tenant/org scope. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "key": "string",
  "kind": "text"
}

Responses

200Definition saved
Content-Type: application/json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "key": "string",
    "kind": "string",
    "configJson": {}
  }
}
400Validation failed
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\",
  \"kind\": \"text\"
}"
DELETE/entities/definitions
Auth requiredentities.definitions.manage

Soft delete custom field definition

Marks the specified definition inactive and tombstones it for the current scope. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "key": "string"
}

Responses

200Definition deleted
Content-Type: application/json
{
  "ok": true,
  "version": null
}
400Missing entity id or key
Content-Type: application/json
{
  "error": "string"
}
404Definition not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
POST/entities/definitions.batch
Auth requiredentities.definitions.manage

Save multiple custom field definitions

Creates or updates multiple definitions for a single entity in one transaction. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "definitions": [
    {
      "key": "string",
      "kind": "text"
    }
  ]
}

Responses

200Definitions saved
Content-Type: application/json
{
  "ok": true,
  "version": null
}
400Validation error
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/definitions.batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"definitions\": [
    {
      \"key\": \"string\",
      \"kind\": \"text\"
    }
  ]
}"
GET/entities/definitions.manage
Auth requiredentities.definitions.manage

Get management snapshot

Returns scoped custom field definitions (including inactive tombstones) for administration interfaces. Requires features: entities.definitions.manage

Parameters

NameInRequiredSchemaDescription
entityIdqueryYesany—

Responses

200Scoped definitions and deleted keys
Content-Type: application/json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "kind": "string",
      "configJson": null,
      "organizationId": null,
      "tenantId": null
    }
  ],
  "deletedKeys": [
    "string"
  ],
  "version": null
}
400Missing entity id
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/definitions.manage?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/entities/definitions.restore
Auth requiredentities.definitions.manage

Restore definition

Reactivates a previously soft-deleted definition within the current tenant/org scope. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "key": "string"
}

Responses

200Definition restored
Content-Type: application/json
{
  "ok": true
}
400Missing entity id or key
Content-Type: application/json
{
  "error": "string"
}
404Definition not found
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/definitions.restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
GET/entities/encryption
Auth requiredentities.definitions.manage

Fetch encryption map

Returns the encrypted field map for the current tenant/organization scope. Requires features: entities.definitions.manage

Parameters

NameInRequiredSchemaDescription
entityIdqueryYesany—

Responses

200Map
Content-Type: application/json
{
  "entityId": "string",
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ],
  "updatedAt": null
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/encryption?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/entities/encryption
Auth requiredentities.definitions.manage

Upsert encryption map

Creates or updates the encryption map for the current tenant/organization scope. Enforces optimistic locking when the caller sends the expected version header. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "tenantId": null,
  "organizationId": null,
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ]
}

Responses

200Saved
Content-Type: application/json
{
  "ok": true,
  "updatedAt": null
}
409Optimistic-lock conflict (stale write)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/encryption" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"tenantId\": null,
  \"organizationId\": null,
  \"fields\": [
    {
      \"field\": \"string\",
      \"hashField\": null
    }
  ]
}"
GET/entities/entities
Auth required

List available entities

Returns generated and custom entities scoped to the caller with field counts per entity.

Responses

200List of entities
Content-Type: application/json
{
  "items": [
    {
      "entityId": "string",
      "source": "code",
      "label": "string",
      "count": 1
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/entities/entities
Auth requiredentities.definitions.manage

Upsert custom entity

Creates or updates a tenant/org scoped custom entity definition. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string",
  "label": "string",
  "description": null,
  "showInSidebar": false,
  "accessRestricted": false
}

Responses

200Entity saved
Content-Type: application/json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "entityId": "string",
    "label": "string"
  }
}
400Validation error
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"label\": \"string\",
  \"description\": null,
  \"showInSidebar\": false,
  \"accessRestricted\": false
}"
DELETE/entities/entities
Auth requiredentities.definitions.manage

Soft delete custom entity

Marks the specified custom entity inactive within the current scope. Requires features: entities.definitions.manage

Request body (application/json)

{
  "entityId": "string"
}

Responses

200Entity deleted
Content-Type: application/json
{
  "ok": true
}
400Missing entity id
Content-Type: application/json
{
  "error": "string"
}
404Entity not found in scope
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\"
}"
GET/entities/entity-settings
Auth requiredentities.definitions.view

Get custom entity settings

Returns the tenant-scoped default-restricted policy for new custom entities. Requires features: entities.definitions.view

Responses

200Current settings
Content-Type: application/json
{
  "newEntitiesRestrictedByDefault": true
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/entity-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/entities/entity-settings
Auth requiredentities.definitions.manage

Update custom entity settings

Sets the tenant-scoped default-restricted policy for new custom entities. Requires features: entities.definitions.manage

Request body (application/json)

{
  "newEntitiesRestrictedByDefault": true
}

Responses

200Updated settings
Content-Type: application/json
{
  "ok": true,
  "newEntitiesRestrictedByDefault": true
}
400Invalid payload
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/entities/entity-settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"newEntitiesRestrictedByDefault\": true
}"
GET/entities/records
Auth requiredentities.records.view

List records

Returns paginated records for the supplied entity. Supports custom field filters, exports, and soft-delete toggles. Requires features: entities.records.view

Parameters

NameInRequiredSchemaDescription
entityIdqueryYesany—
pagequeryNoany—
pageSizequeryNoany—
sortFieldqueryNoany—
sortDirqueryNoany—
searchqueryNoany—
searchFieldsqueryNoany—
withDeletedqueryNoany—
formatqueryNoany—
exportScopequeryNoany—
export_scopequeryNoany—
allqueryNoany—
fullqueryNoany—

Responses

200Paginated records
Content-Type: application/json
{
  "items": [
    {}
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
400Missing entity id
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/records?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/entities/records
Auth requiredentities.records.manage

Create record

Creates a record for the given entity. When `recordId` is omitted or not a UUID the data engine will generate one automatically. Requires features: entities.records.manage

Request body (application/json)

{
  "entityId": "string",
  "values": {}
}

Responses

200Record created
Content-Type: application/json
{
  "ok": true
}
400Validation failure
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"values\": {}
}"
PUT/entities/records
Auth requiredentities.records.manage

Update record

Updates an existing record. If the provided recordId is not a UUID the record will be created instead to support optimistic flows. Requires features: entities.records.manage

Request body (application/json)

{
  "entityId": "string",
  "recordId": "string",
  "values": {}
}

Responses

200Record updated
Content-Type: application/json
{
  "ok": true
}
400Validation failure
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"values\": {}
}"
DELETE/entities/records
Auth requiredentities.records.manage

Delete record

Soft deletes the specified record within the current tenant/org scope. Requires features: entities.records.manage

Request body (application/json)

{
  "entityId": "string",
  "recordId": "string"
}

Responses

200Record deleted
Content-Type: application/json
{
  "ok": true
}
400Missing entity id or record id
Content-Type: application/json
{
  "error": "string"
}
404Record not found
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\"
}"
GET/entities/relations/options
Auth requiredentities.definitions.view

List relation options

Returns up to 200 option entries for populating relation dropdowns, automatically resolving label fields when omitted. An entityId that matches neither an active custom entity nor a registered ORM entity yields an empty option list. Requires features: entities.definitions.view

Parameters

NameInRequiredSchemaDescription
entityIdqueryYesany—
labelFieldqueryNoany—
qqueryNoany—
idsqueryNoany—
routeContextFieldsqueryNoany—

Responses

200Option list
Content-Type: application/json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/relations/options?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/entities/sidebar-entities
Auth required

Get sidebar entities

Returns custom entities flagged with `showInSidebar` for the current tenant/org scope.

Responses

200Sidebar entities for navigation
Content-Type: application/json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "href": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/entities/sidebar-entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Month-End

Showing 6 of 6 endpoints
GET/month_end/packages/{id}/file
Auth requiredmonth_end.view

Stream a built package’s zip, under the name the build gave it

Requires features: month_end.view

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Responses

200The zip
Content-Type: application/zip
string
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such package in this organization, or a malformed id (`package_not_found`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}
409The selected organization is not the one the request says its page shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
500The stored zip is not the one the package was built as (`zip_changed`), or an unexpected failure
Content-Type: application/json
{
  "error": "string"
}
503The zip could not be read from file storage (`storage_unavailable`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/month_end/packages/:id/file" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/month_end/packages/{id}/sent
Auth requiredmonth_end.manage

Mark this package as the one sent for its month; the newest mark wins

The body is `{}` or `{ "confirm": true }`; send the organization the page shows in `x-obp-organization`. A package that is not ready is marked only with the confirmation; without it the answer is 409 and nothing is written. Requires features: month_end.manage

Parameters

NameInRequiredSchemaDescription
idpathYesany—

Request body (application/json)

{}

Responses

200The package as marked (a `PackageSummary`, with `sentAt` and `sentByUserId`)
Content-Type: application/json
{}
400No organization selected, none stated by the page (`organization_not_stated`), or a body that says anything but `{ "confirm": true }` (`invalid_request`)
Content-Type: application/json
{
  "error": "string"
}
404No such package in this organization, or a malformed id (`package_not_found`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}
409The package is not ready and the request did not confirm (`not_ready`), or the selected organization is no longer the one the page shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/month_end/packages/:id/sent" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
GET/month_end/profile
Auth requiredmonth_end.view

The client’s email settings, with the version an edit is made against

Requires features: month_end.view

Responses

200The settings and their `updatedAt`
Content-Type: application/json
{
  "profile": {
    "clientName": "string",
    "recipientName": "string",
    "arContactName": "string",
    "filePrefix": "string",
    "stageRollup": [
      {
        "label": "string",
        "stages": [
          "string"
        ]
      }
    ],
    "excludedStages": [
      "string"
    ],
    "normalisationAccounts": [
      "string"
    ],
    "newLogoDealType": "string",
    "topCustomers": 1,
    "collectionDaysGoal": 1,
    "driverThreshold": 1,
    "didNotRepeatThreshold": 1,
    "departments": [
      {
        "name": "string",
        "short": "string"
      }
    ],
    "senderName": "string"
  },
  "updatedAt": "string"
}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404This client has no settings yet (`no_settings`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}
409The selected organization is not the one the page says it shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}
503The settings are stored but could not be read back (`settings_unreadable`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/month_end/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
PUT/month_end/profile
Auth requiredmonth_end.manage

Validate and save the client’s email settings

The body is `{ "profile": { … } }`. Send the `updatedAt` the settings were read with in the `x-om-ext-optimistic-lock-expected-updated-at` header; a save made against settings that have since changed is answered 409 and changes nothing, and one that names no version is taken only while the client has no settings or has settings that cannot be read back. Send the organization the page shows in `x-obp-organization`. Settings the schema refuses are answered 400 with one problem per line, each at its field. The settings are stored encrypted or not at all. Requires features: month_end.manage

Request body (application/json)

{
  "profile": {
    "clientName": "string",
    "recipientName": "string",
    "arContactName": "string",
    "filePrefix": "string",
    "stageRollup": [
      {
        "label": "string",
        "stages": [
          "string"
        ]
      }
    ],
    "excludedStages": [
      "string"
    ],
    "normalisationAccounts": [
      "string"
    ],
    "newLogoDealType": "string",
    "topCustomers": 1,
    "collectionDaysGoal": 1,
    "driverThreshold": 1,
    "didNotRepeatThreshold": 1,
    "departments": [
      {
        "name": "string",
        "short": "string"
      }
    ],
    "senderName": "string"
  }
}

Responses

200The saved settings and their new `updatedAt`
Content-Type: application/json
{
  "profile": {
    "clientName": "string",
    "recipientName": "string",
    "arContactName": "string",
    "filePrefix": "string",
    "stageRollup": [
      {
        "label": "string",
        "stages": [
          "string"
        ]
      }
    ],
    "excludedStages": [
      "string"
    ],
    "normalisationAccounts": [
      "string"
    ],
    "newLogoDealType": "string",
    "topCustomers": 1,
    "collectionDaysGoal": 1,
    "driverThreshold": 1,
    "didNotRepeatThreshold": 1,
    "departments": [
      {
        "name": "string",
        "short": "string"
      }
    ],
    "senderName": "string"
  },
  "updatedAt": "string"
}
400No organization selected, none stated by the page (`organization_not_stated`), a body that is not `{ "profile": … }` (`invalid_request`), or settings the schema refuses (`invalid_settings`)
Content-Type: application/json
{
  "error": "string"
}
409Saved against a stale version, or lost to another first save (`optimistic_lock_conflict`); saved with no version against settings that read back (`version_required`); or the selected organization is no longer the one the page shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string",
  "code": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}
503Nothing was written: encryption is not set up for the settings (`encryption_not_set_up`), or they would not have been stored encrypted (`not_stored_encrypted`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}

Example

curl -X PUT "https://open-book-partners-erp.northbound.run/api/month_end/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"profile\": {
    \"clientName\": \"string\",
    \"recipientName\": \"string\",
    \"arContactName\": \"string\",
    \"filePrefix\": \"string\",
    \"stageRollup\": [
      {
        \"label\": \"string\",
        \"stages\": [
          \"string\"
        ]
      }
    ],
    \"excludedStages\": [
      \"string\"
    ],
    \"normalisationAccounts\": [
      \"string\"
    ],
    \"newLogoDealType\": \"string\",
    \"topCustomers\": 1,
    \"collectionDaysGoal\": 1,
    \"driverThreshold\": 1,
    \"didNotRepeatThreshold\": 1,
    \"departments\": [
      {
        \"name\": \"string\",
        \"short\": \"string\"
      }
    ],
    \"senderName\": \"string\"
  }
}"
GET/month_end/runs/{runId}/package
Auth requiredmonth_end.view

The package page’s data: the plan, the newest package built from this run, the month’s sent package, and where last month’s figures come from

Requires features: month_end.view

Parameters

NameInRequiredSchemaDescription
runIdpathYesany—

Responses

200The page’s data (a `PackagePageData`, at the top level)
Content-Type: application/json
{}
400No organization selected
Content-Type: application/json
{
  "error": "string"
}
404No such model update in this organization, or a malformed id (`run_not_found`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}
409The selected organization is not the one the page says it shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}
503The client’s settings (`settings_unreadable`) or the package to show (`package_unreadable`) are stored but could not be read back
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/month_end/runs/:runId/package" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/month_end/runs/{runId}/package
Auth requiredmonth_end.manage

Build the reporting package: the zip of the month’s files and the email written beside it

No body; send the organization the page shows in `x-obp-organization`. Answers 201 with the new package’s summary, whether or not the package is ready to send. A build the plan blocks is answered 409 with the plan’s own `blocks`, the same list the page shows. A stored file that cannot be read back, is no longer the file that was uploaded, or does not read as its row is answered 400 naming the row (`component`, `sourceKey`); files holding another company’s QuickBooks report are answered 400 with one line per file and sheet. Whatever the refusal, nothing is kept. Requires features: month_end.manage

Parameters

NameInRequiredSchemaDescription
runIdpathYesany—

Responses

201The package that was built (a `PackageSummary`)
Content-Type: application/json
{}
400No organization selected, or none stated by the page (`organization_not_stated`); a stored file that could not be read (`file_unreadable`); one whose bytes no longer match its upload (`file_changed`); or files holding another company’s QuickBooks report (`file_other_company`)
Content-Type: application/json
{
  "error": "string"
}
404No such model update in this organization, or a malformed id (`run_not_found`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}
409The build is blocked (`blocked`): `blocks` lists why, each with what to do. Or the selected organization is no longer the one the page shows (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}
503Nothing was kept: the settings, or last month’s sent package, are stored but could not be read back (`settings_unreadable`, `package_unreadable`); encryption is not set up for a package’s content (`encryption_not_set_up`, `not_stored_encrypted`); or file storage refused the zip (`storage_unavailable`)
Content-Type: application/json
{
  "error": "string",
  "code": "string",
  "problems": [
    {
      "key": "string",
      "fallback": "string"
    }
  ]
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/month_end/runs/:runId/package" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Perspectives

Showing 4 of 4 endpoints
GET/perspectives/{tableId}
Auth requiredperspectives.use

Load perspectives for a table

Returns personal perspectives and available role defaults for the requested table identifier. Requires features: perspectives.use

Parameters

NameInRequiredSchemaDescription
tableIdpathYesany—

Responses

200Current perspectives and defaults.
Content-Type: application/json
{
  "tableId": "string",
  "perspectives": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "tableId": "string",
      "settings": {},
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null
    }
  ],
  "defaultPerspectiveId": null,
  "rolePerspectives": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "tableId": "string",
      "settings": {},
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null,
      "roleId": "00000000-0000-4000-8000-000000000000",
      "tenantId": null,
      "organizationId": null,
      "roleName": null
    }
  ],
  "manageableRolePerspectives": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "tableId": "string",
      "settings": {},
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null,
      "roleId": "00000000-0000-4000-8000-000000000000",
      "tenantId": null,
      "organizationId": null,
      "roleName": null
    }
  ],
  "roles": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hasPerspective": true,
      "hasDefault": true
    }
  ],
  "canApplyToRoles": true
}
400Invalid table identifier
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/perspectives/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/perspectives/{tableId}
Auth requiredperspectives.use

Create or update a perspective

Saves a personal perspective and optionally applies the same configuration to selected roles. Requires features: perspectives.use

Parameters

NameInRequiredSchemaDescription
tableIdpathYesany—

Request body (application/json)

{
  "name": "string",
  "settings": {}
}

Responses

200Perspective saved successfully.
Content-Type: application/json
{
  "perspective": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "tableId": "string",
    "settings": {},
    "isDefault": true,
    "createdAt": "string",
    "updatedAt": null
  },
  "rolePerspectives": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "tableId": "string",
      "settings": {},
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null,
      "roleId": "00000000-0000-4000-8000-000000000000",
      "tenantId": null,
      "organizationId": null,
      "roleName": null
    }
  ],
  "clearedRoleIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
400Validation failed or invalid roles provided
Content-Type: application/json
{
  "error": "string"
}
409Optimistic lock conflict or perspective name already exists
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/perspectives/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"settings\": {}
}"
DELETE/perspectives/{tableId}/{perspectiveId}
Auth requiredperspectives.use

Delete a personal perspective

Removes a perspective owned by the current user for the given table. Requires features: perspectives.use

Parameters

NameInRequiredSchemaDescription
tableIdpathYesany—
perspectiveIdpathYesany—

Responses

200Perspective removed.
Content-Type: application/json
{
  "success": true
}
400Invalid identifiers supplied
Content-Type: application/json
{
  "error": "string"
}
404Perspective not found
Content-Type: application/json
{
  "error": "string"
}
409Optimistic lock conflict
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/perspectives/string/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
DELETE/perspectives/{tableId}/roles/{roleId}
Auth requiredperspectives.role_defaults

Clear role perspectives for a table

Removes all role-level perspectives associated with the provided role identifier for the table. Requires features: perspectives.role_defaults

Parameters

NameInRequiredSchemaDescription
tableIdpathYesany—
roleIdpathYesany—

Request body (application/json)

"string"

Responses

200Role perspectives cleared.
Content-Type: application/json
{
  "success": true
}
400Invalid identifiers supplied
Content-Type: application/json
{
  "error": "string"
}
404Role not found in scope
Content-Type: application/json
{
  "error": "string"
}
409Optimistic lock conflict
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X DELETE "https://open-book-partners-erp.northbound.run/api/perspectives/string/roles/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

QuickBooks reports

Showing 1 of 1 endpoints
POST/qbo_reports/imports
Auth requiredqbo_reports.manage

Upload a QuickBooks report export (.xlsx or .xlsm, multipart/form-data) and index it against the organization's confirmed company (qbo_reports.manage)

Requires features: qbo_reports.manage

Responses

201The record outcome and the landing attempt
Content-Type: application/json
{}
400No organization selected, no file, not a spreadsheet, or a report that could not be indexed
Content-Type: application/json
{
  "error": "string"
}
409The organization's books come from another source, the report names another company, or the page that sent it shows another organization than the one now selected (`organization_changed`)
Content-Type: application/json
{
  "error": "string"
}
413Upload exceeds 25 MB
Content-Type: application/json
{
  "error": "string"
}
500Unexpected failure
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/qbo_reports/imports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

Query Index

Showing 3 of 3 endpoints
POST/query_index/purge
Auth requiredquery_index.purge

Purge query index records

Queues a purge job to remove indexed records for an entity type within the active scope. Requires features: query_index.purge

Request body (application/json)

{
  "entityType": "string"
}

Responses

200Purge job accepted.
Content-Type: application/json
{
  "ok": true
}
400Missing entity type
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/query_index/purge" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
POST/query_index/reindex
Auth requiredquery_index.reindex

Trigger query index rebuild

Queues a reindex job for the specified entity type within the current tenant scope. Requires features: query_index.reindex

Request body (application/json)

{
  "entityType": "string"
}

Responses

200Reindex job accepted.
Content-Type: application/json
{
  "ok": true
}
400Missing entity type
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/query_index/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
GET/query_index/status
Auth requiredquery_index.status.view

Inspect query index coverage

Returns entity counts comparing base tables with the query index along with the latest job status. Requires features: query_index.status.view

Responses

200Current query index status.
Content-Type: application/json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "baseCount": null,
      "indexCount": null,
      "vectorCount": null,
      "ok": true,
      "job": {
        "status": "idle",
        "startedAt": null,
        "finishedAt": null,
        "heartbeatAt": null,
        "processedCount": null,
        "totalCount": null,
        "scope": null
      }
    }
  ],
  "errors": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "stack": null,
      "payload": null,
      "occurredAt": "string"
    }
  ],
  "logs": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "level": "info",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "details": null,
      "occurredAt": "string"
    }
  ]
}
400Tenant or organization context required
Content-Type: application/json
{
  "error": "string"
}

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/query_index/status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"

QuickBooksSync

Showing 4 of 4 endpoints
GET/sync_quickbooks/connection
Auth requiredsync_quickbooks.view

The current organization's QuickBooks connection: company, realm, last error

Requires features: sync_quickbooks.view

Responses

200Connection summary (no secrets)
Content-Type: application/json
"string"
400No organization selected
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/sync_quickbooks/connection" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
GET/sync_quickbooks/oauth/callback

QuickBooks OAuth callback — consume state, refuse a company held elsewhere, exchange code, persist tokens

Responses

200Success response
Content-Type: application/json
"string"
302Redirect to the QuickBooks integration page, which shows the outcome
Content-Type: application/json
"string"

Example

curl -X GET "https://open-book-partners-erp.northbound.run/api/sync_quickbooks/oauth/callback" \
  -H "Accept: application/json"
POST/sync_quickbooks/oauth/disconnect
Auth requiredsync_quickbooks.connect

Disconnect the current organization's QuickBooks company (revoke at Intuit, then forget it here)

Requires features: sync_quickbooks.connect

Responses

200{ disconnected: true, revoke } — revoke is revoked | shared | already_ended | nothing_to_revoke | failed
Content-Type: application/json
"string"
400No organization selected
Content-Type: application/json
"string"
500Could not disconnect
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/sync_quickbooks/oauth/disconnect" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
POST/sync_quickbooks/oauth/initiate
Auth requiredsync_quickbooks.connect

Start a QuickBooks Online OAuth connect flow

Requires features: sync_quickbooks.connect

Responses

200Authorize URL returned; a state nonce was persisted
Content-Type: application/json
"string"
409OAuth client app not configured for this workspace
Content-Type: application/json
"string"
500Failed to start the connection
Content-Type: application/json
"string"

Example

curl -X POST "https://open-book-partners-erp.northbound.run/api/sync_quickbooks/oauth/initiate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"