Openwork Organizations API
Top-level organization creation and context routes.
Documentation
Documentation
https://openworklabs.com/docs
APIReference
https://openworklabs.com/docs/api-reference
Top-level organization creation and context routes.
openapi: 3.1.0
info:
title: Den Admin Organizations API
description: 'OpenAPI spec for the Den control plane API.
Authentication:
- Use `Authorization: Bearer <session-token>` for user-authenticated routes that require a Den session.
- Use `x-api-key: <den-api-key>` for API-key-authenticated routes that accept organization API keys.
- Public routes like health and documentation do not require authentication.
Swagger tip: use the security schemes in the Authorize dialog to set either `bearerAuth` or `denApiKey` before trying protected endpoints.'
version: dev
servers:
- url: https://api.openworklabs.com
tags:
- name: Organizations
description: Top-level organization creation and context routes.
paths:
/v1/org:
patch:
operationId: patchV1Org
tags:
- Organizations
summary: Update organization
description: Updates organization fields. Workspace admins can change allowed desktop versions; all other fields remain owner-only. The slug is immutable to avoid breaking dashboard URLs.
responses:
'200':
description: Organization updated successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/OrganizationResponse'
'400':
description: The organization update request body was invalid, contained malformed email domains, or contained an invalid brand icon URL.
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateOrganizationBadRequest'
'401':
description: The caller must be signed in to update an organization.
content:
application/json:
schema:
$ref: '#/components/schemas/UnauthorizedError'
'402':
description: Enabling enforced SSO or desktop version controls requires an Enterprise plan.
content:
application/json:
schema:
$ref: '#/components/schemas/EnterprisePlanRequiredError'
'403':
description: The caller does not have permission to update the requested organization fields.
content:
application/json:
schema:
$ref: '#/components/schemas/ForbiddenError'
'404':
description: The organization could not be found.
content:
application/json:
schema:
$ref: '#/components/schemas/NotFoundError'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
minLength: 2
maxLength: 120
allowedEmailDomains:
anyOf:
- maxItems: 100
type: array
items:
type: string
minLength: 1
maxLength: 255
- type: 'null'
allowedDesktopVersions:
anyOf:
- maxItems: 200
type: array
items:
type: string
minLength: 1
maxLength: 32
- type: 'null'
requireSso:
type: boolean
brandAppName:
anyOf:
- type: string
minLength: 1
maxLength: 64
- type: 'null'
brandLogoUrl:
anyOf:
- type: string
maxLength: 2048
format: uri
- type: 'null'
brandIconUrl:
anyOf:
- type: string
maxLength: 2048
format: uri
- type: 'null'
brandAccentColor:
anyOf:
- type: string
minLength: 1
maxLength: 32
- type: 'null'
get:
operationId: getV1Org
tags:
- Organizations
summary: Get active organization
description: Returns the active organization from the current session, including its owner, the current member record, and their team memberships.
responses:
'200':
description: Organization context returned successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/OrganizationContextResponse'
'401':
description: The caller must be signed in to load organization context.
content:
application/json:
schema:
$ref: '#/components/schemas/UnauthorizedError'
'404':
description: The organization could not be found.
content:
application/json:
schema:
$ref: '#/components/schemas/NotFoundError'
/v1/brand-assets/{organizationId}/{kind}/{version}:
get:
operationId: getV1BrandAssetsByOrganizationIdByKindByVersion
tags:
- Organizations
summary: Read an immutable organization brand asset
description: Serves a capability-signed, content-addressed organization logo or app icon from this Den deployment.
responses:
'200':
description: Immutable brand image bytes.
'404':
description: The managed brand asset could not be found.
content:
application/json:
schema:
$ref: '#/components/schemas/NotFoundError'
parameters:
- schema:
type: string
in: path
name: organizationId
required: true
- schema:
type: string
in: path
name: kind
required: true
- schema:
type: string
in: path
name: version
required: true
/v1/org/brand-assets:
post:
operationId: postV1OrgBrandAssets
tags:
- Organizations
summary: Upload organization brand assets
description: Validates and stores owner-supplied wordmark and app icon files inside the Den deployment.
responses:
'200':
description: Managed brand assets were saved.
content:
application/json:
schema:
$ref: '#/components/schemas/ManagedBrandAssetUploadResponse'
'400':
description: A supplied brand asset was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidManagedBrandAssetError'
'401':
description: The caller must be signed in.
content:
application/json:
schema:
$ref: '#/components/schemas/UnauthorizedError'
'403':
description: Only workspace owners can upload brand assets.
content:
application/json:
schema:
$ref: '#/components/schemas/ForbiddenError'
'413':
description: The upload exceeded the request size limit.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidManagedBrandAssetError'
/v1/orgs/{organizationId}/install-links:
post:
operationId: postV1OrgsByOrganizationIdInstallLinks
tags:
- Organizations
summary: Create organization install link
description: Mints a shareable OpenWork desktop install link for a signed-in organization member. Older active links remain valid unless an owner or admin explicitly requests rotation.
responses:
'200':
description: Install link created successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/CreateInstallLinkResponse'
'400':
description: The install-link request was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidRequestError'
'401':
description: The caller must be signed in to create install links.
content:
application/json:
schema:
$ref: '#/components/schemas/UnauthorizedError'
'403':
description: The organization needs the installLinks capability enabled, and only workspace owners and admins can rotate existing links.
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/ForbiddenError'
- $ref: '#/components/schemas/CapabilityDisabledError'
'404':
description: The organization could not be found.
content:
application/json:
schema:
$ref: '#/components/schemas/NotFoundError'
'429':
description: The member has created too many install links.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitedError'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateInstallLinkRequest'
parameters:
- schema:
type: string
in: path
name: organizationId
required: true
/v1/install-config:
get:
operationId: getV1InstallConfig
tags:
- Organizations
summary: Resolve install-link configuration
description: Returns organization setup details and a fresh desktop connection handoff for a valid install link token.
responses:
'200':
description: Install configuration resolved successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/InstallExperienceConfig'
'400':
description: The install-link token was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidRequestError'
'404':
description: The install link was missing, expired, or revoked.
content:
application/json:
schema:
$ref: '#/components/schemas/InstallLinkNotFoundError'
'429':
description: Too many install-link attempts.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitedError'
parameters:
- in: query
name: token
schema:
type: string
maxLength: 255
pattern: ^[A-Za-z0-9_-]{8,}$
required: true
/v1/install-connect/preview:
post:
operationId: postV1InstallConnectPreview
tags:
- Organizations
summary: Preview desktop connection
description: Resolves a short-lived organization connection code without consuming it.
responses:
'200':
description: Desktop connection resolved successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantResponse'
'400':
description: The connection code body was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidRequestError'
'404':
description: The connection code was not found.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'409':
description: The connection code was already consumed.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'410':
description: The connection code expired.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'429':
description: Too many connection attempts.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitedError'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
pattern: ^[A-Za-z0-9_-]{24,128}$
required:
- code
/v1/install-connect/exchange:
post:
operationId: postV1InstallConnectExchange
tags:
- Organizations
summary: Accept desktop connection
description: Consumes a short-lived organization connection code exactly once.
responses:
'200':
description: Desktop connection resolved successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantResponse'
'400':
description: The connection code body was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidRequestError'
'404':
description: The connection code was not found.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'409':
description: The connection code was already consumed.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'410':
description: The connection code expired.
content:
application/json:
schema:
$ref: '#/components/schemas/DesktopConnectGrantFailure'
'429':
description: Too many connection attempts.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitedError'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
pattern: ^[A-Za-z0-9_-]{24,128}$
required:
- code
/v1/install/{platform}:
get:
operationId: getV1InstallByPlatform
tags:
- Organizations
summary: Download OpenWork desktop
description: Streams an explicitly provisioned standard OpenWork installer or redirects directly to the configured standard release. Organization setup remains a separate Den deep-link step.
responses:
'200':
description: Installer artifact returned successfully.
content:
text/plain:
schema:
type: string
'302':
description: Den redirected the browser to a verified normal desktop download.
'400':
description: The install-link token or platform was invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/InvalidRequestError'
'404':
description: The install link was missing, expired, or revoked.
content:
application/json:
schema:
$ref: '#/components/schemas/InstallLinkNotFoundError'
'429':
description: Too many installer download attempts.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitedError'
parameters:
- in: query
name: token
schema:
type: string
maxLength: 255
pattern: ^[A-Za-z0-9_-]{8,}$
required: true
- schema:
type: string
in: path
name: platform
required: true
components:
schemas:
RateLimitedError:
type: object
properties:
error:
type: string
const: rate_limited
message:
type: string
required:
- error
- message
CreateInstallLinkRequest:
type: object
properties:
rotate:
default: false
type: boolean
OrganizationResponse:
type: object
properties:
organization:
anyOf:
- type: object
properties: {}
additionalProperties: {}
- type: 'null'
required:
- organization
ConnectLinkClaims:
type: object
properties:
iss:
type: string
format: uri
aud:
type: string
const: openwork-desktop-connect
iat:
type: integer
minimum: 0
maximum: 9007199254740991
exp:
type: integer
minimum: 0
maximum: 9007199254740991
jti:
type: string
minLength: 8
v:
type: number
const: 1
org:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 128
required:
- name
brand:
type: object
properties:
appName:
type: string
minLength: 1
maxLength: 64
logoUrl:
anyOf:
- type: string
format: uri
- type: 'null'
iconUrl:
anyOf:
- type: string
format: uri
- type: 'null'
required:
- appName
- logoUrl
- iconUrl
den:
type: object
properties:
baseUrl:
type: string
format: uri
apiBaseUrl:
anyOf:
- type: string
format: uri
- type: 'null'
required:
- baseUrl
requireSignin:
type: boolean
required:
- iss
- aud
- iat
- exp
- jti
- v
- org
- brand
- den
- requireSignin
EnterprisePlanRequiredError:
type: object
properties:
error:
type: string
const: enterprise_plan_required
feature:
type: string
message:
type: string
required:
- error
- feature
- message
InvalidBrandIconError:
type: object
properties:
error:
type: string
const: invalid_brand_icon
reason:
type: string
message:
type: string
required:
- error
- reason
- message
UpdateOrganizationBadRequest:
anyOf:
- $ref: '#/components/schemas/InvalidRequestError'
- $ref: '#/components/schemas/InvalidEmailDomainError'
- $ref: '#/components/schemas/InvalidBrandIconError'
OrganizationOwner:
type: object
properties:
memberId:
description: Den TypeID with 'om_' prefix and a 26-character base32 suffix.
format: typeid
type: string
minLength: 29
maxLength: 29
pattern: ^om_.*
userId:
description: Den TypeID with 'usr_' prefix and a 26-character base32 suffix.
format: typeid
type: string
minLength: 30
maxLength: 30
pattern: ^usr_.*
name:
anyOf:
- type: string
- type: 'null'
email:
anyOf:
- type: string
format: email
pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
- type: 'null'
image:
anyOf:
- type: string
- type: 'null'
required:
- memberId
- userId
- name
- email
UnauthorizedError:
type: object
properties:
error:
type: string
const: unauthorized
required:
- error
InstallExperienceConfig:
type: object
properties:
appName:
default: OpenWork
type: string
minLength: 1
maxLength: 64
clientName:
type: string
minLength: 1
webUrl:
type: string
format: uri
apiUrl:
type: string
format: uri
requireSignin:
type: boolean
logoUrl:
anyOf:
- type: string
format: uri
- type: 'null'
iconUrl:
default: null
anyOf:
- type: string
format: uri
- type: 'null'
connectUrl:
type: string
connectExpiresAt:
type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
required:
- clientName
- webUrl
- apiUrl
- requireSignin
- logoUrl
- connectUrl
- connectExpiresAt
NotFoundError:
type: object
properties:
error:
type: string
message:
type: string
required:
- error
InvalidEmailDomainError:
type: object
properties:
error:
type: string
const: invalid_email_domain
message:
type: string
invalidDomains:
type: array
items:
type: string
required:
- error
- message
- invalidDomains
ForbiddenError:
type: object
properties:
error:
type: string
enum:
- forbidden
- reauth
reason:
type: string
message:
type: string
required:
- error
InstallLinkNotFoundError:
type: object
properties:
error:
type: string
const: install_link_not_found
required:
- error
InvalidManagedBrandAssetError:
type: object
properties:
error:
type: string
const: invalid_brand_asset
kind:
anyOf:
- type: string
enum:
- logo
- icon
- type: 'null'
reason:
type: string
message:
type: string
required:
- error
- kind
- reason
- message
DesktopConnectGrantFailure:
type: object
properties:
error:
type: string
enum:
- connect_grant_invalid
- connect_grant_expired
- connect_grant_replayed
required:
- error
CapabilityDisabledError:
type: object
properties:
error:
type: string
const: capability_disabled
capability:
type: string
enum:
- installLinks
- mcpConnections
required:
- error
- capability
DesktopConnectGrantResponse:
type: object
properties:
claims:
$ref: '#/components/schemas/ConnectLinkClaims'
required:
- claims
InvalidRequestError:
type: object
properties:
error:
type: string
const: invalid_request
details:
type: array
items:
type: object
properties:
message:
type: string
path:
type: array
items:
anyOf:
- type: string
- type: number
required:
- message
additionalProperties: {}
required:
- error
- details
CreateInstallLinkResponse:
type: object
properties:
token:
type: string
installPageUrl:
type: string
format: uri
required:
- token
- installPageUrl
OrganizationContextResponse:
type: object
properties:
organization:
type: object
properties:
owner:
anyOf:
- $ref: '#/components/schemas/OrganizationOwner'
- type: 'null'
additionalProperties: {}
currentMember:
type: object
properties: {}
additionalProperties: {}
currentMemberTeams:
type: array
items:
type: object
properties: {}
additionalProperties: {}
required:
- organization
- currentMember
- currentMemberTeams
additionalProperties: {}
ManagedBrandAssetUploadResponse:
type: object
properties:
assets:
type: object
properties:
logo:
type: object
properties:
kind:
type: string
enum:
- logo
- icon
version:
type: string
pattern: ^[a-f0-9]{64}$
extension:
type: string
enum:
- png
- jpg
contentType:
type: string
enum:
- image/png
- image/jpeg
url:
type: string
format: uri
width:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
height:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
byteLength:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
originalName:
type: string
uploadedAt:
type: string
required:
- kind
- version
- extension
- contentType
- url
- width
- height
- byteLength
- originalName
- uploadedAt
icon:
type: object
properties:
kind:
type: string
enum:
- logo
- icon
version:
type: string
pattern: ^[a-f0-9]{64}$
extension:
type: string
enum:
- png
- jpg
contentType:
type: string
enum:
- image/png
- image/jpeg
url:
type: string
format: uri
width:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
height:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
byteLength:
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
originalName:
type: string
uploadedAt:
type: string
required:
- kind
- version
- extension
- contentType
- url
- width
- height
- byteLength
- originalName
- uploadedAt
required:
- assets
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: session-token
description: 'Session token passed as `Authorization: Bearer <session-token>` for user-authenticated Den routes.'
denApiKey:
type: apiKey
in: header
name: x-api-key
description: Organization API key passed as the `x-api-key` header for API-key-authenticated Den routes.