HTTP API reference
This is the reference for the Port OS REST API, for controlling and managing devices and groups.
The Port OS API is designed around REST. Our API has resource-based URLs, accepts JSON request bodies, returns JSON-encoded responses, and uses standard HTTP status codes, authentication, and verbs.
You can use Port OS with your account from anywhere, whether you're working in the cloud or on your computer.
Just getting started? Please see API quick start, or Guide for agents for more. See Simulators to create a test device or Tokens to create a test token.
Prefer a small compact index? See the compact guide.
Want to integrate devices with AI agents? Please see the Port OS MCP guide, or agent-specific guides.
Port OS uses authentication tokens to access APIs. Customers that want to access Port OS can either create one-off access tokens or use OAuth 2.0.
Customers that want to use Port OS for services like AI or websites should use OAuth 2.0 to connect to Port OS. See the official OAuth 2.0 guide for how OAuth works. To use the Port OS APIs, you must register an OAuth client.
Authentication is how a client gets a token.
Port OS supports these OAuth flows:
- website auth: authorization code flow with PKCE
- device auth: device code flow
- cloud AI agent auth: CIMD (Client ID Metadata Documents)
- legacy cloud AI agent auth: DCR (Dynamic Client Registration) — prefer CIMD, DCR is deprecated.
Every flow uses the OAuth endpoints listed beside this text: discover the server from its metadata, obtain a token from the token endpoint, then send it on each API call in the Authorization header as a bearer token.
Authorization is separate from authentication: it decides what a token may do once the client has one. Port OS has a two-layer authorization system:
- each token has a set of scopes (see OAuth permission scopes) that set which APIs it can use
- each resource has access control rules that govern who can use it.
Access controls let customers write granular rules, such as:
- only bob may see this light
- only claude may see the battery level of my devices
See Access control for more.
RESTful APIs on Port OS require permission scopes. When performing an OAuth exchange with the Port OS OAuth endpoints, include the scopes your requests need.
Each endpoint in the Port OS RESTful interface lists the scope it requires.
Port OS uses a two-layer authorization mechanism: the first layer is the scope permissions on the token, and the second is a series of granular access control rules. See Access control for more.
Pagination
APIs in Port OS paginate. A list call returns one page of results, not everything, so to read the rest you call the API again for each following page. The maxResults parameter sets the page size (1 through 100, default 100).
- Call the list operation, with your filters and optionally
maxResults. LeavenextTokenout. - Read the page from
results. The response also carries apaginationContextobject. - If
paginationContext.nextTokenis present, there may be more results. Call the same operation again with the same filters and that value as thenextTokenparameter. - Repeat from step 2 until a response has no
paginationContext.nextToken. At that point you have read every result.
Treat the token as opaque: pass it back exactly as received and keep the same filters and page size between calls. A page can be empty and still carry a nextToken, so stop only when the token is absent. Operations that take a request body, such as the endpoint query, send maxResults and nextToken in the body's paginationContext instead of the query string. The example here uses GET /endpoints.
Parameters
Response fields
- results
array<Endpoint>Required - enablement
stringRequired Whether Port OS uses an endpoint or group. A disabled one is hidden from collections by default and cannot be sent device messages or targeted by them; exact authorized reads still return it. Rediscovery never changes it, so disabling is how a user hides a discovered device or room for good.
Default:
ENABLEDAllowed values: ENABLED, DISABLED.
- id
stringRequired The unique identifier for the endpoint
pattern: "^port1/[^/]+/[^/]+/endpoints/[^/]+$"
- manufacturer
TypeValue The manufacturer of the route as a human readable string or a localizable asset.
- serialNumber
TypeValue The serial number of the route as a human readable string or a localizable asset.
- parentEndpointId
string The endpoint this endpoint is part of, when a provider reports it as a component of another endpoint (for example a SmartThings oven cavity of its range). Derived from `IS_PART_OF` relationships between routes; when routes disagree the lowest parent endpoint ID is returned. Absent when the endpoint has no parent. Endpoint lists leave such endpoints out unless `includeChildEndpoints` is true; read one by its ID.
pattern: "^port1/[^/]+/[^/]+/endpoints/[^/]+$"
- childEndpointIds
array<string> The IDs of the endpoints that are part of this endpoint, sorted. Absent when the endpoint has no children.
- resourceGroups
array<ResourceGroup> List of associated resource groups
- permissions
array<Permission> List of permissions attached to the resource group
- resourceType
stringRequired The type of resource this permission grants access to
Allowed values: flows, flownodes, flowports, flowlinks, flow-runs, principals, systems, accounts, access-control-rules, tokens, secrets, auth-links, oauth-clients, plugins, endpoints, endpoint-simulators, endpoint-groups, virtual-endpoint-groups, endpoint-group-routes, routes, device-interfaces, application-interfaces, messages, subscriptions, plugin-subscription, views.
- resourceType
- permissions
- routes
array<Route> List of associated routes
- pluginProfileId
string Stable profile ID selected from the registered plugin declaration. Required for V2 plugin discovery; retained with the route for admission and presentation.
minLength: 1
- id
stringRequired The persisted Port OS route identifier. Synchronized routes can receive a backend-generated, owner-scoped value here even when the discovery payload supplied an explicit route `id`; do not use this field as the stable plugin-owned route identity unless the synchronizing implementation explicitly documents preservation. Provider discovery and synchronization input use ProviderDiscoveredRoute, whose optional advisory ID does not carry this persisted-identity rule.
pattern: "^port1/[^/]+/[^/]+/routes/[^/]+$"
- associatedPluginRouteId
stringRequired The stable plugin-owned route identifier associated with this Port OS route. For synchronized plugin capability contracts and fixture parity checks, use this field to match a route from enumeration back to the discovery payload.
- name
TypeValue The name of the route as a human readable string or a localizable asset. This is either a raw string or a localizable asset reference.
- interfaces
array<RouteInterface>Required List of interfaces supported by the route. These denote what the route is capable of performing such as turning off/on, etc.
- ontologicalTypes
array<string>Required List of ontological types for the route. These are used mostly for customers to know what an endpoint is, like if its a lightbulb, or a speaker, etc.
- pluginCustomMetadata
object A set of custom metadata that the plugin uses and attaches to the route, this is meant specifically for the plugin to use.
- manufacturer
TypeValue The manufacturer of the route as a human readable string or a localizable asset.
- pluginProfileId
- attributes
array<AttributeState> Device state attributes for this endpoint. Query responses populate this array when `GET /endpoints` is called with `expand=interfaces.attributes`; otherwise it remains the default shallow shape.
- retrievability
stringRequired Whether the serving route supports retrieving this attribute.
Allowed values: RETRIEVABLE, NOT_RETRIEVABLE.
- autoUpdateFrequency
stringRequired Declared frequency at which the serving route automatically updates this attribute.
Allowed values: REALTIME, MINUTES, HOURLY, DAILY, NEVER.
- error
Error The one Port OS error shape, returned by every REST error response and every MCP tool error. `code` names the specific failure and `family` its broad class; both are closed enums, so clients branch on them and show `message`, which says what failed and what to do next.
- family
stringRequired The broad class of an error. The family decides the HTTP status of a REST error, so clients can handle every code of a family alike.
Allowed values: NOT_FOUND, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, CONFLICT, GONE, PRECONDITION_FAILED, PRECONDITION_REQUIRED, PAYLOAD_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE, UNPROCESSABLE_ENTITY, LOCKED, TOO_MANY_REQUESTS, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, NOT_IMPLEMENTED, INTERNAL_SERVER_ERROR, REQUEST_INVALID, AUTHENTICATION, PERMISSION, ENDPOINT_NOT_FOUND, ENDPOINT_STATE_CONFLICT, ENDPOINT_UNAVAILABLE, RATE_LIMIT, PROVIDER_BAD_RESPONSE, PROVIDER_UNAVAILABLE, PROVIDER_TIMEOUT.
- code
stringRequired The specific error, one of a closed set of Port OS codes. Every REST error body, MCP tool error, batch item error and discovery item error uses this enum; each code belongs to an `ErrorFamily` that decides its HTTP status. Provider-specific codes are never codes: they travel in `details.providerCode`. New failure kinds are added here, never as free strings.
Allowed values: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, INSUFFICIENT_SCOPE, NOT_FOUND, CONFLICT, UNPROCESSABLE_ENTITY, UNSUPPORTED_OPERATION, TOO_MANY_REQUESTS, SERVICE_UNAVAILABLE, NOT_IMPLEMENTED, INTERNAL, CHECKSUM_MISMATCH, FILE_TOO_LARGE, STORAGE_UNAVAILABLE, INSUFFICIENT_PERMISSIONS, INVALID_FILE_TYPE, QUOTA_EXCEEDED, CONCURRENT_MODIFICATION, ENDPOINT_UNREACHABLE, BRIDGE_UNREACHABLE, CLOUD_CONTROL_DISABLED, ENDPOINT_BUSY, ENDPOINT_LOW_POWER, AUTHORIZATION_CREDENTIAL_INVALID, AUTHORIZATION_CREDENTIAL_EXPIRED, INVALID_DIRECTIVE, INVALID_VALUE, VALUE_OUT_OF_RANGE, NOT_SUPPORTED_IN_CURRENT_MODE, RATE_LIMIT_EXCEEDED, PROVIDER_SERVICE_UNAVAILABLE, PROVIDER_TIMEOUT, PROVIDER_INTERNAL_ERROR, INVALID_PROVIDER_RESPONSE, OUTCOME_UNKNOWN, PRECONDITION_FAILED, AUTH_LINK_NOT_READY, AUTH_LINK_NOT_DISCONNECTABLE, PRECONDITION_REQUIRED, IDEMPOTENCY_KEY_REQUIRED, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_FLIGHT, IDEMPOTENCY_KEY_INVALID, GROUP_MERGED, MULTI_ROOM_UNSUPPORTED, MAP_NOT_ACTIVE, TARGET_UNSUPPORTED, TARGET_AMBIGUOUS, TARGET_GROUP_NOT_FOUND, TARGET_GROUP_DISABLED, DUPLICATE_TARGET_GROUP, MESSAGE_UNSUPPORTED, MESSAGE_AMBIGUOUS, ENDPOINT_DISABLED, ENDPOINT_ROUTE_CHANGED, ENDPOINT_HAS_DEPENDENCIES, ENDPOINT_EVENT_SUBSCRIPTIONS_ACTIVE, ENDPOINT_COMMITTED_CLEANUP_PENDING, DEEP_QUERY_FAILED, PROVIDER_NO_STATE, EVENT_MANAGEMENT_DISABLED, SUBSCRIPTION_IDENTITY_IMMUTABLE, UNSUPPORTED_MEDIA_TYPE, EVENT_PAYLOAD_TOO_LARGE, REQUEST_BODY_TOO_LARGE, EVENT_IDENTITY_CONFLICT, EVENT_INGRESS_QUOTA_EXCEEDED, EVENT_RECEIPT_EXPIRED, EVENT_STORAGE_UNAVAILABLE, EVENT_DEAD_LETTER_NOT_FOUND, EVENT_REDRIVE_CONFLICT, EVENT_REDRIVE_EXPIRED, RECEIPT_CONTEXT_EXPIRED, EVENT_MAINTENANCE_INCOMPLETE, UNKNOWN_INTERFACE, INVALID_CONFIGURATION, INVALID_ATTRIBUTE, INVALID_PROVIDER_MESSAGE, UNDECLARED_CAPABILITY, PLUGIN_ROUTE_ID_CONFLICT, UNRESOLVED_REFERENCE, INVALID_RELATIONSHIP, RELATIONSHIP_CYCLE, MULTIPLE_PARENTS, FLOW_VALIDATION_FAILED.
- details
ErrorDetails Optional typed context for an error. Fields are additive and only populated when the error can safely expose that context to API clients.
- discoveryEndpointId
string Port OS provider endpoint (type ENDPOINT_PLUGIN) of the connection to send `discover-routes` to when `code` is `TARGET_UNSUPPORTED` because the route must be rediscovered.
- retryAfterSeconds
integer Suggested retry delay for rate-limit or provider-unavailable failures.
minimum: 0
- providerCode
string The third-party provider's own error code, for support. It is opaque and provider-specific; branch on `code` and `family` instead.
- discoveryEndpointId
- errors
array<SynchronizationItemError> Every rejected item when a request that is validated item by item and applied atomically fails, such as a discovery `SynchronizationRequest` whose routes, group routes or relationships are rejected. Omitted for single-item failures.
One rejected discovery item of a `SynchronizationRequest`, keyed by the plugin-owned identity of the rejected item: `pluginRouteId` for a route or group route, or `relationshipExternalId` for a relationship. Exactly one of the two is set. It shares the `code`, `family`, `status` and `message` fields of `BatchItemError`; discovery items are keyed by plugin-owned identity because they carry no `requestId`. A request with any rejected item is rejected as a whole and lists every rejected item.
- pluginRouteId
string Plugin-owned identifier of the rejected route or group route. Set unless the item is a relationship.
- relationshipExternalId
string Plugin-owned `externalId` of the rejected relationship. Set only for relationship items.
- interface
string Canonical capability interface name the failure applies to, when the failure is interface-specific.
- version
string Declared capability interface version the failure applies to, when the failure is interface-specific.
- code
stringRequired The specific error, one of a closed set of Port OS codes. Every REST error body, MCP tool error, batch item error and discovery item error uses this enum; each code belongs to an `ErrorFamily` that decides its HTTP status. Provider-specific codes are never codes: they travel in `details.providerCode`. New failure kinds are added here, never as free strings.
Allowed values: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, INSUFFICIENT_SCOPE, NOT_FOUND, CONFLICT, UNPROCESSABLE_ENTITY, UNSUPPORTED_OPERATION, TOO_MANY_REQUESTS, SERVICE_UNAVAILABLE, NOT_IMPLEMENTED, INTERNAL, CHECKSUM_MISMATCH, FILE_TOO_LARGE, STORAGE_UNAVAILABLE, INSUFFICIENT_PERMISSIONS, INVALID_FILE_TYPE, QUOTA_EXCEEDED, CONCURRENT_MODIFICATION, ENDPOINT_UNREACHABLE, BRIDGE_UNREACHABLE, CLOUD_CONTROL_DISABLED, ENDPOINT_BUSY, ENDPOINT_LOW_POWER, AUTHORIZATION_CREDENTIAL_INVALID, AUTHORIZATION_CREDENTIAL_EXPIRED, INVALID_DIRECTIVE, INVALID_VALUE, VALUE_OUT_OF_RANGE, NOT_SUPPORTED_IN_CURRENT_MODE, RATE_LIMIT_EXCEEDED, PROVIDER_SERVICE_UNAVAILABLE, PROVIDER_TIMEOUT, PROVIDER_INTERNAL_ERROR, INVALID_PROVIDER_RESPONSE, OUTCOME_UNKNOWN, PRECONDITION_FAILED, AUTH_LINK_NOT_READY, AUTH_LINK_NOT_DISCONNECTABLE, PRECONDITION_REQUIRED, IDEMPOTENCY_KEY_REQUIRED, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_FLIGHT, IDEMPOTENCY_KEY_INVALID, GROUP_MERGED, MULTI_ROOM_UNSUPPORTED, MAP_NOT_ACTIVE, TARGET_UNSUPPORTED, TARGET_AMBIGUOUS, TARGET_GROUP_NOT_FOUND, TARGET_GROUP_DISABLED, DUPLICATE_TARGET_GROUP, MESSAGE_UNSUPPORTED, MESSAGE_AMBIGUOUS, ENDPOINT_DISABLED, ENDPOINT_ROUTE_CHANGED, ENDPOINT_HAS_DEPENDENCIES, ENDPOINT_EVENT_SUBSCRIPTIONS_ACTIVE, ENDPOINT_COMMITTED_CLEANUP_PENDING, DEEP_QUERY_FAILED, PROVIDER_NO_STATE, EVENT_MANAGEMENT_DISABLED, SUBSCRIPTION_IDENTITY_IMMUTABLE, UNSUPPORTED_MEDIA_TYPE, EVENT_PAYLOAD_TOO_LARGE, REQUEST_BODY_TOO_LARGE, EVENT_IDENTITY_CONFLICT, EVENT_INGRESS_QUOTA_EXCEEDED, EVENT_RECEIPT_EXPIRED, EVENT_STORAGE_UNAVAILABLE, EVENT_DEAD_LETTER_NOT_FOUND, EVENT_REDRIVE_CONFLICT, EVENT_REDRIVE_EXPIRED, RECEIPT_CONTEXT_EXPIRED, EVENT_MAINTENANCE_INCOMPLETE, UNKNOWN_INTERFACE, INVALID_CONFIGURATION, INVALID_ATTRIBUTE, INVALID_PROVIDER_MESSAGE, UNDECLARED_CAPABILITY, PLUGIN_ROUTE_ID_CONFLICT, UNRESOLVED_REFERENCE, INVALID_RELATIONSHIP, RELATIONSHIP_CYCLE, MULTIPLE_PARENTS, FLOW_VALIDATION_FAILED.
- family
stringRequired The broad class of an error. The family decides the HTTP status of a REST error, so clients can handle every code of a family alike.
Allowed values: NOT_FOUND, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, CONFLICT, GONE, PRECONDITION_FAILED, PRECONDITION_REQUIRED, PAYLOAD_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE, UNPROCESSABLE_ENTITY, LOCKED, TOO_MANY_REQUESTS, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, NOT_IMPLEMENTED, INTERNAL_SERVER_ERROR, REQUEST_INVALID, AUTHENTICATION, PERMISSION, ENDPOINT_NOT_FOUND, ENDPOINT_STATE_CONFLICT, ENDPOINT_UNAVAILABLE, RATE_LIMIT, PROVIDER_BAD_RESPONSE, PROVIDER_UNAVAILABLE, PROVIDER_TIMEOUT.
- pluginRouteId
- family
- sampledAt
string Device or provider reported sample time in UTC. Omitted when the source did not report a sample time; fetching the value does not make an unknown or old sample fresh. Compare with `freshness`, which says whether the sample is recent enough to treat as current.
format: "date-time"
- freshness
string Whether the value is current enough to present as the device's present state. `FRESH`: the provider reported a sample within the last 30 seconds, or a forced refresh (`forceDeviceAttributeQuery=true` or `forceDeviceQuery`) read the provider during this request and the provider reported no sample time. `STALE`: the value is older than 30 seconds, came from stored state, or is a provider's own last known value. A forced refresh cannot make a provider's old sample newer: when the provider (for example a thermostat reached through a voice-assistant account) answers with the sample it already holds, the value keeps its original `sampledAt` and is `STALE`. Report the value together with `sampledAt`, never as the present reading.
Allowed values: FRESH, STALE.
- retrievability
- enablement
Error Handling
Use the HTTP status to identify whether a request succeeded. A failed request can return a JSON error with a human-readable message and structured fields for handling and debugging.
Check the operation's documented responses for its specific errors. Plugin dispatch failures can use additional error types, families, and typed details.
Error fields
- code
stringRequired The specific error, one of a closed set of Port OS codes. Every REST error body, MCP tool error, batch item error and discovery item error uses this enum; each code belongs to an `ErrorFamily` that decides its HTTP status. Provider-specific codes are never codes: they travel in `details.providerCode`. New failure kinds are added here, never as free strings.
Allowed values: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, INSUFFICIENT_SCOPE, NOT_FOUND, CONFLICT, UNPROCESSABLE_ENTITY, UNSUPPORTED_OPERATION, TOO_MANY_REQUESTS, SERVICE_UNAVAILABLE, NOT_IMPLEMENTED, INTERNAL, CHECKSUM_MISMATCH, FILE_TOO_LARGE, STORAGE_UNAVAILABLE, INSUFFICIENT_PERMISSIONS, INVALID_FILE_TYPE, QUOTA_EXCEEDED, CONCURRENT_MODIFICATION, ENDPOINT_UNREACHABLE, BRIDGE_UNREACHABLE, CLOUD_CONTROL_DISABLED, ENDPOINT_BUSY, ENDPOINT_LOW_POWER, AUTHORIZATION_CREDENTIAL_INVALID, AUTHORIZATION_CREDENTIAL_EXPIRED, INVALID_DIRECTIVE, INVALID_VALUE, VALUE_OUT_OF_RANGE, NOT_SUPPORTED_IN_CURRENT_MODE, RATE_LIMIT_EXCEEDED, PROVIDER_SERVICE_UNAVAILABLE, PROVIDER_TIMEOUT, PROVIDER_INTERNAL_ERROR, INVALID_PROVIDER_RESPONSE, OUTCOME_UNKNOWN, PRECONDITION_FAILED, AUTH_LINK_NOT_READY, AUTH_LINK_NOT_DISCONNECTABLE, PRECONDITION_REQUIRED, IDEMPOTENCY_KEY_REQUIRED, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_FLIGHT, IDEMPOTENCY_KEY_INVALID, GROUP_MERGED, MULTI_ROOM_UNSUPPORTED, MAP_NOT_ACTIVE, TARGET_UNSUPPORTED, TARGET_AMBIGUOUS, TARGET_GROUP_NOT_FOUND, TARGET_GROUP_DISABLED, DUPLICATE_TARGET_GROUP, MESSAGE_UNSUPPORTED, MESSAGE_AMBIGUOUS, ENDPOINT_DISABLED, ENDPOINT_ROUTE_CHANGED, ENDPOINT_HAS_DEPENDENCIES, ENDPOINT_EVENT_SUBSCRIPTIONS_ACTIVE, ENDPOINT_COMMITTED_CLEANUP_PENDING, DEEP_QUERY_FAILED, PROVIDER_NO_STATE, EVENT_MANAGEMENT_DISABLED, SUBSCRIPTION_IDENTITY_IMMUTABLE, UNSUPPORTED_MEDIA_TYPE, EVENT_PAYLOAD_TOO_LARGE, REQUEST_BODY_TOO_LARGE, EVENT_IDENTITY_CONFLICT, EVENT_INGRESS_QUOTA_EXCEEDED, EVENT_RECEIPT_EXPIRED, EVENT_STORAGE_UNAVAILABLE, EVENT_DEAD_LETTER_NOT_FOUND, EVENT_REDRIVE_CONFLICT, EVENT_REDRIVE_EXPIRED, RECEIPT_CONTEXT_EXPIRED, EVENT_MAINTENANCE_INCOMPLETE, UNKNOWN_INTERFACE, INVALID_CONFIGURATION, INVALID_ATTRIBUTE, INVALID_PROVIDER_MESSAGE, UNDECLARED_CAPABILITY, PLUGIN_ROUTE_ID_CONFLICT, UNRESOLVED_REFERENCE, INVALID_RELATIONSHIP, RELATIONSHIP_CYCLE, MULTIPLE_PARENTS, FLOW_VALIDATION_FAILED.
- family
stringRequired The broad class of an error. The family decides the HTTP status of a REST error, so clients can handle every code of a family alike.
Allowed values: NOT_FOUND, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, CONFLICT, GONE, PRECONDITION_FAILED, PRECONDITION_REQUIRED, PAYLOAD_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE, UNPROCESSABLE_ENTITY, LOCKED, TOO_MANY_REQUESTS, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, NOT_IMPLEMENTED, INTERNAL_SERVER_ERROR, REQUEST_INVALID, AUTHENTICATION, PERMISSION, ENDPOINT_NOT_FOUND, ENDPOINT_STATE_CONFLICT, ENDPOINT_UNAVAILABLE, RATE_LIMIT, PROVIDER_BAD_RESPONSE, PROVIDER_UNAVAILABLE, PROVIDER_TIMEOUT.
- details
ErrorDetails Optional typed context for an error. Fields are additive and only populated when the error can safely expose that context to API clients.
- discoveryEndpointId
string Port OS provider endpoint (type ENDPOINT_PLUGIN) of the connection to send `discover-routes` to when `code` is `TARGET_UNSUPPORTED` because the route must be rediscovered.
- retryAfterSeconds
integer Suggested retry delay for rate-limit or provider-unavailable failures.
minimum: 0
- providerCode
string The third-party provider's own error code, for support. It is opaque and provider-specific; branch on `code` and `family` instead.
- discoveryEndpointId
Recovering from errors
Correct invalid requests before retrying. For authentication failures, obtain a valid token; for permission failures, check access to the resource.
Retry temporary server failures with backoff only when the operation is safe to repeat. Check for an existing result before repeating a request that creates or changes a resource.
Versioning
Port OS uses a single evolving HTTP API contract. Resources such as /endpoints do not require a /v1 or /v2 URL prefix.
New endpoints, optional parameters, and response fields can be introduced as the API evolves. Read the fields your integration uses and tolerate additional fields in a response.
Compatible changes
Adding an optional field allows existing consumers to continue reading the fields they already understand. Avoid relying on JSON property order, and handle unfamiliar enum values deliberately.
Deprecated fields
When an incompatible change is necessary, a replacement is introduced alongside the existing field and the migration is documented. Check the API reference for deprecations before adopting a replacement.
Capability interface versions
Capability interfaces declare their own version. This identifies the capability contract and is separate from the HTTP API's evolution.
Use the declared interface version when constructing capability messages. See the capability reference for each interface's commands, events, state, and configuration.
Devices
Device APIs read and control the devices and groups in your account.
An endpoint is a device or service you can act on, such as a phone, TV, lightbulb or vacuum.
/endpointsList the endpoints that a customer has access to. For example, you enumerate to list the customer's lights, vacuums, or similar devices.
Endpoints contain information such as:
- The endpoint's name, description, and other top-level data.
- The capabilities the endpoint has, like power, camera, etc.
- The state of the endpoint.
Usage of the API is controlled by the query parameters. By default it lists enabled endpoints owned by the authenticated user. Type and serialNumber filters stay within that scope. An explicit owner can select another owner's endpoints when the caller can read them.
An exact id lookup takes precedence over the owner, type, serialNumber, and enablement filters. It can return a readable shared endpoint, including a disabled one, but still requires endpoint-read permission.
To retrieve the state of a specific endpoint, send id=<endpoint-id>
together with expand=interfaces.attributes. Add
forceDeviceAttributeQuery=true to refresh that state from the device
before the response is returned.
Required scope
endpoint:readQuery parameters
- enablement
string Filter the results by enablement. Enablement is a customer setting that stops Port OS from using a resource without deleting it. Port OS uses it in place of deletion because it cannot delete a device on the provider's side. Defaults to ENABLED. Use DISABLED to list only disabled resources, or '*' to include both enabled and disabled resources. Looking up a resource by its exact id ignores this filter.
Default:
ENABLEDAllowed values: ENABLED, DISABLED, *.
- owner
string Filter the results by the owner of the resource. Defaults to the authenticated caller's resources when omitted. Use '~caller' to make that scope explicit. Another owner can be selected only for resources the caller can read.
- id
string The unique identifier of a specific endpoint to retrieve. When provided, this exact-ID filter takes precedence over owner, type, serialNumber and enablement filters. It can return a shared or disabled endpoint when the caller has endpoint-read permission.
- nextToken
string Opaque server-issued continuation token. Repeat the same selection, page size, expansion, force-refresh setting and authenticated grant. Modified, expired or differently bound tokens return 400; restart without nextToken.
minLength: 1
- expand
array<string> Expand determines what data we retrieve for each endpoint. Use `interfaces.attributes` to populate each endpoint's `attributes` array so you can, for example, find their states. When omitted, the response returns only a limited set of data.
uniqueItems: true
- includeChildEndpoints
boolean A list returns root endpoints only: an endpoint that is part of another endpoint (for example an oven cavity of its range, which reports `parentEndpointId`) is left out so one physical device is one entry, and the parent lists its parts in `childEndpointIds`. Send `true` to list those parts too. An exact `id` lookup always returns the endpoint it names, part or not. A part whose parent the caller cannot read is still listed.
Default:
false- forceDeviceAttributeQuery
boolean Forces a live device-backed attribute refresh for endpoint-state queries. This makes Port OS call into the device to get its data, if the device provider does not update the state automatically. This flag is only applied when `expand` includes `interfaces.attributes`. Send `true` to force the refresh; absent or `false` values use stored state. Invalid values, or `true` without the required expansion, return 400. A forced read asks the provider; it cannot make a provider's old sample newer. Each returned attribute carries `sampledAt` and `freshness`: treat a `STALE` value as the provider's last known reading, not the present state. When the provider answers a forced read but gives no value for a declared, requested attribute, that attribute is returned with no `value` and an `error` whose code is `PROVIDER_NO_STATE`. It is not omitted and the request does not fail; treat the state as unknown.
Returns
200
Endpoints retrieved successfully. Responses without expand=interfaces.attributes return the default shallow endpoint shape. Responses with that expansion populate deep attribute state in each endpoint's attributes array. If endpoint enumeration succeeds but one or more forced deep-query items fail, the response can include an errors array describing those partial state-refresh failures; request-level failures still use HTTP error responses.
Fields
Errors
400 BAD_REQUESTMissing or malformed endpoint query parameters
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpointsCreates or updates an endpoint.
Required scope
endpoint:writeReturns
200
Endpoint upserted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpointsApply endpoint partial metadata, enablement or merge changes in one SQLite transaction. At least one field besides id is required. An empty 202 is returned only after commit; clients may requery metadata. Endpoint updated notifications are best effort. A merge cannot cross owners or silently rewrite unsupported dependencies; conflicts roll back all changes.
Required scope
endpoint:writeRequest body
Fields
- enablement
string Whether Port OS uses an endpoint or group. A disabled one is hidden from collections by default and cannot be sent device messages or targeted by them; exact authorized reads still return it. Rediscovery never changes it, so disabling is how a user hides a discovered device or room for good.
Default:
ENABLEDAllowed values: ENABLED, DISABLED.
Returns
202
Endpoint modification committed; empty response.
No response body.
Errors
400Invalid partial update or incompatible merge.
Check the request fields and any validation details, then correct the payload.
401Authentication required.
Obtain a valid access token, then retry.
403Endpoint management or dependency permission denied.
Check the linked account and its permissions for the device or resource.
404Endpoint not found.
Verify the target identifier and that its endpoint or plugin route still exists.
409Dependent state prevents merge; nothing changed.
Check the device’s current mode or state. Retry only after the conflict is resolved.
500Modification failed before commit.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
503ENDPOINT_COMMITTED_CLEANUP_PENDING. The modification committed, but required active-work cleanup is pending. Re-read source and target before retrying.
Wait for the provider or bridge to recover, then retry safely with backoff.
/endpointsDeletes an endpoint.
Required scope
endpoint:writeReturns
200
Endpoint deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpoint-queryReturn a bounded page of authorized endpoints matching the shared Query expression. An empty query and filters such as type or interfaces default to enabled endpoints owned by the caller. An explicit ownerId, groupId, or required exact id selector can include readable endpoints owned by others; endpoint-read permission is still required for each result. Collections exclude disabled endpoints unless enablement is filtered, while a query that requires specific exact endpoint IDs can return disabled endpoint metadata without an enablement filter. See the request schema for supported fields and operators. Use groupId to select members of a readable room/group, then intersect type or declared interfaces to select lights. Group predicates additionally require group:read for delegated OAuth. Repeat the complete query settings and grant when following nextToken; cursors expire after fifteen minutes. Device attributes are returned only when expanded. Report per-target command acceptance and observed state separately; querying a group does not grant permission to control its members.
Required scope
endpoint:readWhen using groupId: group:read
Request body
Fields
- query
QueryRequired A bounded shared Query expression. By default, an empty object and filters such as type or interfaces select enabled endpoints owned by the caller. An explicit ownerId, groupId, or required exact id selector can include readable endpoints owned by others; endpoint-read permission is still required for every result. This endpoint supports match, and, or, and not. Scalar match keys are id, name, type, ownerId, manufacturer, model, serialNumber, and enablement. interfaces matches membership in the endpoint's declared interface names; groupId matches membership in a readable endpoint group. Comparisons, patterns, freeform search, unknown keys, ambiguous nodes, expressions deeper than eight levels, or more than 64 nodes return 400. Values are exact strings. Collections exclude disabled endpoints unless the expression filters enablement. A query that requires specific exact endpoint IDs can also return disabled endpoint metadata without an enablement filter, subject to endpoint-read permission. A groupId predicate additionally requires group:read for delegated OAuth and read permission on that group.
- expand
array<string> Optional reported state expansion; only interfaces.attributes is supported.
uniqueItems: true
- includeChildEndpoints
boolean A query returns root endpoints only: an endpoint that is part of another endpoint (for example an oven cavity of its range, which reports `parentEndpointId`) is left out, and the parent lists its parts in `childEndpointIds`. Send true to match those parts too. A query that selects endpoints by exact id or by groupId returns them either way. Repeat the same setting when following nextToken. A part whose parent the caller cannot read is still returned.
Default:
false- forceDeviceQuery
boolean Refresh reported device attributes for the selected page. Requires expand to contain interfaces.attributes. An accepted command is not proof of device state; inspect each attribute's sampledAt and freshness and the partial errors. A provider can answer with its own last known sample, which is returned as STALE. Disabled endpoints cannot be forcibly refreshed.
Default:
false
Returns
200
Endpoints retrieved successfully
Fields
Errors
400 BAD_REQUESTInvalid query, unsupported expansion, or invalid/expired/mismatched cursor.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENMissing conditional OAuth scope or permission to read a referenced group.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDA referenced group does not exist.
Verify the target identifier and that its endpoint or plugin route still exists.
409 ENDPOINT_DISABLEDA selected disabled endpoint cannot be forcibly refreshed.
Check the device’s current mode or state. Retry only after the conflict is resolved.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Port OS allows you to send messages to things. By sending messages to endpoints, customers can turn off lights, play music, etc. A message is an object that is sent around in port OS between different endpoints.
/messagesSends a message to a recipient.
Plugin-originated dispatch errors use the canonical plugin dispatch error
contract: type, family, code, message, and optional typed details.
Fire-and-forget requests can only return plugin errors detected before the
request is accepted. Synchronous requests can also map a correlated
portos/errorResponse plugin response into the HTTP error statuses below.
Required scope
message:sendRequest body
Fields
- message
151 variantsRequired A capability message to send to an endpoint: exactly one of the inbound capability messages. The header namespace, name and version select the variant and the body schema.
- responseType
string How to handle the request. AUTO resolves the actual selected route's current plugin declaration: synchronous operations wait for a correlated response; asynchronous and fire-and-forget operations return dispatch acceptance without claiming device execution. Legacy integrations retain synchronous behavior in AUTO mode. Omission retains the existing FIRE_FORGET default.
POST /messages: application/json request body: possible enum values for responseType Value Meaning SYNC— FIRE_FORGET— AUTOSelect the operating mode automatically.
Returns
200
Request accepted; an asynchronous acknowledgment may have an empty messageId and no message. Verify device state separately.
Fields
- messageId
stringRequired Identifies the unary response message when one is returned. An accepted asynchronous request can return an empty string with no message. This field does not provide a durable dispatch receipt, retry deduplication or proof of observed device state.
- message
56 variants The message that was responded with when there was a unary message response, exactly one of the outbound capability messages. A synchronous send can return one; an acknowledgment with no message back leaves this empty. The header namespace, name and version select the variant and the body schema.
Errors
400 INVALID_VALUEInvalid request payload or plugin dispatch request error
Check the request fields and any validation details, then correct the payload.
Example details
Example details { "providerCode": "provider-invalid-speed", "parameter": "speed", "actual": "storm" }401 AUTHORIZATION_CREDENTIAL_EXPIREDPlugin authorization credential is invalid or expired
Re-link the plugin to renew its provider credentials, then retry.
Example details
Example details { "providerCode": "provider-token-expired" }403 INSUFFICIENT_PERMISSIONSPlugin provider denied the requested operation
Check the linked account and its permissions for the device or resource.
Example details
Example details { "providerCode": "provider-scope-missing" }404 NOT_FOUNDTarget endpoint or plugin route was not found
Verify the target identifier and that its endpoint or plugin route still exists.
409 NOT_SUPPORTED_IN_CURRENT_MODEEndpoint state conflicts with the requested message
Check the device’s current mode or state. Retry only after the conflict is resolved.
Example details
Example details { "providerCode": "device-mode-conflict", "currentMode": "eco" }422 VALUE_OUT_OF_RANGEPlugin rejected a semantically invalid message value
Correct the rejected value using the constraints in the error details or request schema.
Example details
Example details { "providerCode": "brightness-out-of-range", "parameter": "brightness", "minimum": 1, "maximum": 100, "actual": "150" }423 ENDPOINT_UNREACHABLEEndpoint is unavailable, unreachable, locked, or low power
Check device connectivity, power, and lock state before retrying.
Example details
Example details { "providerCode": "device-offline", "pluginId": "port1.plugin.hue", "pluginRouteId": "hue-device-42" }429 RATE_LIMIT_EXCEEDEDPlugin provider rate limit exceeded
Wait for Retry-After or details.retryAfterSeconds when supplied; otherwise use backoff.
Example details
Example details { "providerCode": "provider-rate-limit", "retryAfterSeconds": 60 }500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
502 PROVIDER_INTERNAL_ERRORPlugin provider returned an invalid or internal error response
Check provider availability. Retry with backoff only when the operation is safe to repeat.
Example details
Example details { "providerCode": "provider-internal-error" }503 PROVIDER_SERVICE_UNAVAILABLEPlugin provider or bridge is unavailable
Wait for the provider or bridge to recover, then retry safely with backoff.
Example details
Example details { "providerCode": "vendor-outage" }504 PROVIDER_TIMEOUTPlugin provider timed out
Check whether the command completed before resending it; a timeout can leave the result uncertain.
Example details
Example details { "providerCode": "provider-timeout" }
All documented plugin error types
| Plugin error family | Representative types | HTTP status |
|---|---|---|
REQUEST_INVALID | INVALID_DIRECTIVE, INVALID_VALUE | 400 |
REQUEST_INVALID | VALUE_OUT_OF_RANGE | 422 |
AUTHENTICATION | AUTHORIZATION_CREDENTIAL_INVALID, AUTHORIZATION_CREDENTIAL_EXPIRED | 401 |
PERMISSION | INSUFFICIENT_PERMISSIONS, CLOUD_CONTROL_DISABLED | 403 |
ENDPOINT_NOT_FOUND | no such endpoint or route | 404 |
ENDPOINT_STATE_CONFLICT | ENDPOINT_BUSY, NOT_SUPPORTED_IN_CURRENT_MODE | 409 |
ENDPOINT_UNAVAILABLE | ENDPOINT_UNREACHABLE, ENDPOINT_LOW_POWER | 423 |
RATE_LIMIT | RATE_LIMIT_EXCEEDED | 429 |
PROVIDER_BAD_RESPONSE | PROVIDER_INTERNAL_ERROR | 502 |
PROVIDER_UNAVAILABLE | BRIDGE_UNREACHABLE, PROVIDER_SERVICE_UNAVAILABLE | 503 |
PROVIDER_TIMEOUT | PROVIDER_TIMEOUT | 504 |
An endpoint group is a way to group a set of endpoints.
/endpoint-groupsLists all available endpoint groups for the authenticated user.
Required scope
group:readQuery parameters
- enablement
string Filter the results by enablement. Enablement is a customer setting that stops Port OS from using a resource without deleting it. Port OS uses it in place of deletion because it cannot delete a device on the provider's side. Defaults to ENABLED. Use DISABLED to list only disabled resources, or '*' to include both enabled and disabled resources. Looking up a resource by its exact id ignores this filter.
Default:
ENABLEDAllowed values: ENABLED, DISABLED, *.
- owner
string Filter the results by the owner of the resource. Defaults to the authenticated caller's resources when omitted. Use '~caller' to make that scope explicit. Another owner can be selected only for resources the caller can read.
- nextToken
string Opaque token for the next page of results. Pass the continuation token returned by the previous response, unchanged. Omit it to start from the first page.
- maxResults
integer Maximum number of results to return in one page. Defaults to 100. Values from 1 through 100 are accepted.
minimum: 1
maximum: 100
Default:
100
Errors
400 BAD_REQUESTBad request - missing required parameters
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpoint-groupsCreates a new endpoint group.
Required scope
group:manageRequest body
Fields
- item
CreateEndpointGroupItemRequired The client-supplied fields of a new endpoint group. Members are added afterwards with `add-members` or created together through `/endpoint-groups-manage` `CREATE_GROUP`. A user group name is `{"type": "PLAIN", "value": ...}` whose value is non-blank UTF-8 text of at most 256 bytes with no control characters; anything else is rejected with BAD_REQUEST.
Errors
400 BAD_REQUESTBad request - invalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpoint-groups-manageApplies a bounded list (max 20) of endpoint group management operations atomically in one transaction.
The operations are CREATE_GROUP, DELETE_GROUP, SET_NAME, ADD_MEMBERS, REMOVE_MEMBERS and MERGE_INTO. Every operation that modifies an existing group carries the group's etag, which is compared inside the transaction.
Item outcomes are returned with HTTP 200 in a status/results/errors envelope correlated by requestId; non-200 statuses are reserved for request-level failures. Idempotency-Key is required unless validateOnly is true; a committed response is replayed for 24 hours for the same principal, key and payload.
Physical control is never part of this request; use POST /messages.
Required scope
group:manageHeader parameters
- Idempotency-Key
stringRequired Client-chosen key that makes a non-idempotent request retry-safe, following draft-ietf-httpapi-idempotency-key-header. A committed response is stored for 24 hours and replayed for the same principal, operation, key and payload. Missing key returns 400 IDEMPOTENCY_KEY_REQUIRED, a key longer than 255 characters returns 400 IDEMPOTENCY_KEY_INVALID, the same key with a different payload returns 422, and the same key while the first request is still in flight returns 409. Failed (rolled-back) requests do not consume the key.
minLength: 1
maxLength: 255
Returns
200
Batch evaluated. status is COMMITTED when every operation was applied (or would be, for validateOnly), otherwise FAILED with every failing operation in errors and nothing written.
Fields
Errors
400 IDEMPOTENCY_KEY_REQUIREDMalformed request, unknown operation, duplicate `requestId`, or missing (`IDEMPOTENCY_KEY_REQUIRED`) or over-long (`IDEMPOTENCY_KEY_INVALID`) `Idempotency-Key`
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - token lacks the group:manage scope
Check the linked account and its permissions for the device or resource.
409 IDEMPOTENCY_KEY_IN_FLIGHTA request with the same `Idempotency-Key` is still in flight
Check the device’s current mode or state. Retry only after the conflict is resolved.
422 IDEMPOTENCY_KEY_REUSEDThe `Idempotency-Key` was already used with a different payload
Correct the rejected value using the constraints in the error details or request schema.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/endpoint-groups/{groupId}/set-nameSets the name of an endpoint group.
Required scope
group:manageHeader parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Returns
202
Endpoint group name update accepted
No response body.
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDEndpoint group not found
Verify the target identifier and that its endpoint or plugin route still exists.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current group version; re-read the group and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/endpoint-groups/{groupId}Get an endpoint group by ID.
Required scope
group:readQuery parameters
- expand
array<string> Expansion tokens that request related or computed fields for the returned resource. Use `interfaces.attributes` to populate the group's `attributes` array with the stored values of its group routes.
- forceDeviceAttributeQuery
boolean Asks each provider for fresh values of the group routes before the response is returned. Only applied when `expand` includes `interfaces.attributes`. Absent, invalid or `false` values return the stored values. A forced read includes only attributes refreshed by that request. If a provider cannot be reached or rejects the request, the request returns `500 DEEP_QUERY_FAILED` instead of presenting stored values as fresh. If a provider answers but gives no value for a declared attribute, that attribute is returned without a `value` and with an `error` whose code is `PROVIDER_NO_STATE`; the request still succeeds.
Returns
200
Endpoint group retrieved successfully
Fields
- id
stringRequired The unique identifier for the endpoint group
pattern: "^port1/[^/]+/[^/]+/endpoint-groups/[^/]+$"
- enablement
stringRequired Whether Port OS uses an endpoint or group. A disabled one is hidden from collections by default and cannot be sent device messages or targeted by them; exact authorized reads still return it. Rediscovery never changes it, so disabling is how a user hides a discovered device or room for good.
Default:
ENABLEDAllowed values: ENABLED, DISABLED.
- etag
stringRequired Opaque strong entity tag of this group version, for example `"12"`. It changes on any committed write to the group, its group routes or its relationships. Send it as `If-Match` (or as the operation `etag` in `/endpoint-groups-manage`) when mutating the group.
- redirectedFrom
string Set when the group was read through a merged-away ID: the ID that was requested, which now redirects to this group. Mutations must use this group's `id`.
- parentGroupId
string The canonical parent group derived from `IS_PART_OF` relationships, when the group has one.
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDEndpoint group not found
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/endpoint-groups/{groupId}Delete an endpoint group by ID.
Required scope
group:manageHeader parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Returns
202
Endpoint group deletion accepted
No response body.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDEndpoint group not found
Verify the target identifier and that its endpoint or plugin route still exists.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current group version; re-read the group and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/endpoint-groups/{groupId}/add-membersAdds one or more endpoint members to an existing endpoint group.
Required scope
group:manageHeader parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Returns
202
Members addition accepted
No response body.
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDEndpoint group not found
Verify the target identifier and that its endpoint or plugin route still exists.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current group version; re-read the group and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/endpoint-groups/{groupId}/remove-membersRemoves one or more endpoint members from an existing endpoint group.
Required scope
group:manageHeader parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Returns
202
Members removal accepted
No response body.
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDEndpoint group not found
Verify the target identifier and that its endpoint or plugin route still exists.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current group version; re-read the group and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Typed relationships between port resources.
/relationshipsRead-only enumeration of typed relationships between endpoints, routes, groups and group routes. Both vertices must be readable by the caller. Defaults to caller-owned relationships with a bounded page. Provider discovery and group membership operations manage the edges.
Required scope
group:readQuery parameters
- owner
string Filter the results by the owner of the resource. Defaults to the authenticated caller's resources when omitted. Use '~caller' to make that scope explicit. Another owner can be selected only for resources the caller can read.
- type
string Only relationships of this type.
Allowed values: IS_PART_OF, CAN_CONTROL, LOCATED_IN, MEMBER_OF.
- nextToken
string Opaque token for the next page of results. Pass the continuation token returned by the previous response, unchanged. Omit it to start from the first page.
Errors
400 BAD_REQUESTBad request - missing or invalid parameters
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Device plugins
Device plugin APIs connect providers and route their devices into Port OS.
A route is a part of an endpoint that denotes what types of interfaces the endpoint can support from a specific plugin. Additionally, a route may describe the endpoint with metadata such as names, manufacturer name, model data, etc.
/routesLists routes owned by the authenticated caller by default. Supplying an owner selects that owner's routes, subject to the caller's route-read permissions. The default page size is 10 and the maximum is 100. The returned route id is the persisted Port OS route identifier. For routes created through synchronization, use associatedPluginRouteId to match the stable plugin-owned or fixture route identity from the discovery payload.
Required scope
endpoint:readQuery parameters
- owner
string Owner whose routes to list. Defaults to the authenticated caller when omitted. A different owner is permitted only for routes the caller can read.
- expand
array<string> Expansion tokens that request related or computed fields for each result. When omitted, the response contains only the default, shallower set of fields for each result.
Errors
400 BAD_REQUESTInvalid query parameter, including an invalid maxResults value or pagination token.
Check the request fields and any validation details, then correct the payload.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/routesDeletes a route.
Required scope
endpoint:writeReturns
200
Route deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/routesCreates or updates a route.
Required scope
endpoint:writeReturns
200
Route upserted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/discoveryTriggers route synchronization for a plugin.
When a plugin connected to Port OS wants to report the routes available on it, it dispatches a discovery payload. The server reconciles the reported routes with its internal state.
For newly persisted synchronized routes, the server can assign a backend-generated, owner-scoped Port OS route id; a client-supplied route id in the discovery payload is advisory and is not the stable lookup surface for plugin capability parity. Consumers that need to match synchronized plugin or fixture routes after enumeration should use associatedPluginRouteId, which preserves the plugin-owned route identity.
Every route and group-route interface must name a capability interface and exact version from the capability-interface library, its configuration must conform to that interface's configuration schema (an omitted configuration is validated as an empty object), and any declared attribute must exist on the interface with a value that conforms to its attribute schema. Any nonconforming item rejects the whole request with 400; errors lists every rejected item keyed by pluginRouteId. Nothing is persisted.
Group routes and relationships are keyed by plugin-owned identity within the plugin and auth link and are created or updated, never removed because they are omitted. A pluginRouteId must be unique across routes and group routes (PLUGIN_ROUTE_ID_CONFLICT). Group-route members and relationship vertices must resolve to routes or group routes of the same plugin and auth link, committed or in the same request (UNRESOLVED_REFERENCE).
IS_PART_OF joins two group routes and is acyclic (RELATIONSHIP_CYCLE) with at most one parent per group route (MULTIPLE_PARENTS); CAN_CONTROL and LOCATED_IN join a route to a group route; self-edges and other shapes are INVALID_RELATIONSHIP. Only CAN_CONTROL carries interfaces, which must name interfaces and inbound messages the source route declares (MESSAGE_UNSUPPORTED). Relationship items are keyed by relationshipExternalId.
A newly reported group route creates its own endpoint group named after it; rediscovery keeps group IDs, merges and user renames.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
Returns
200
Routes synchronized successfully
No response body.
Errors
400 BAD_REQUESTBad request — invalid payload, or one or more routes or group routes do not conform to the capability-interface library
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized — missing or invalid bearer token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden — insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
An endpoint group route is a way for a gropu of devices to be controlled at the same time.
/group-routesAPI to enumerate the group routes that a principal has permissions to.
Required scope
group:readQuery parameters
- owner
string Filter the results by the owner of the resource. Defaults to the authenticated caller's resources when omitted. Use '~caller' to make that scope explicit. Another owner can be selected only for resources the caller can read.
- nextToken
string Opaque token for the next page of results. Pass the continuation token returned by the previous response, unchanged. Omit it to start from the first page.
Errors
400 BAD_REQUESTBad request - missing required parameters
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/group-routes/{groupId}Deletes a group route by ID.
Required scope
group:manageReturns
200
Group route deleted successfully
No response body.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDGroup route not found
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
A plugin is a receiver of messages. A plugin could be an HTTP connection, android device messenger, an MQTT connection, or anything else. Messages sent to endpoints are propagated through plugins.
/pluginsLists all available plugins
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
Errors
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/pluginsCreates a new plugin
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/pluginsDeletes a plugin
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Plugin deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Auth Links are a way to link an endpoint to a plugin.
/auth-linksLists all auth links
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
- status
string Collection visibility. Defaults to CONNECTED. Use DRAFT to list incomplete drafts, or '*' to include all authorized auth links. Exact id reads ignore this filter.
Default:
CONNECTEDAllowed values: DRAFT, CONNECTED, *.
- expand
array<string> Expansion tokens that request related or computed fields for each result. When omitted, the response contains only the default, shallower set of fields for each result.
Errors
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/auth-linksCreates a new auth link
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/auth-linksDeletes an auth link
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Auth link deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/auth-link-draftsCreates a DRAFT auth link for a plugin authorization variant and regionalization. A draft holds the customer-owned OAuth client and provider configuration while the customer completes setup; it is not usable by endpoints until the provider connection is established. Requires the plugin-establish-link permission, evaluated with the variant and regionalization as operation context. The same Idempotency-Key and payload replays the committed response.
Required scope
Any authenticated access token
Header parameters
- Idempotency-Key
stringRequired Client-chosen key that makes a non-idempotent request retry-safe, following draft-ietf-httpapi-idempotency-key-header. A committed response is stored for 24 hours and replayed for the same principal, operation, key and payload. Missing key returns 400 IDEMPOTENCY_KEY_REQUIRED, a key longer than 255 characters returns 400 IDEMPOTENCY_KEY_INVALID, the same key with a different payload returns 422, and the same key while the first request is still in flight returns 409. Failed (rolled-back) requests do not consume the key.
minLength: 1
maxLength: 255
Request body
Fields
Returns
201
Draft auth link created
Fields
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
409 CONFLICTThe Idempotency-Key request is still in flight
Check the device’s current mode or state. Retry only after the conflict is resolved.
422 UNPROCESSABLE_ENTITYThe Idempotency-Key was already used with a different payload
Correct the rejected value using the constraints in the error details or request schema.
/auth-links/oauth-client-credentialsLists metadata of the caller's own OAuth client secrets that the caller's auth links for the plugin reference, never the secret values. Only links the caller may read are considered. The list is self-scoped and needs no plugin-establish-link permission.
Required scope
Any authenticated access token
Query parameters
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
/{namespace}/{principalType}/{principalId}/auth-links/{authLinkId}/update-draftUpdates a DRAFT auth link. Omitted fields are unchanged. The link must still be a DRAFT and the If-Match etag must match its current version; the wildcard If-Match: * is not accepted. Requires the plugin-establish-link permission, evaluated with the variant and regionalization as operation context.
Required scope
Any authenticated access token
Header parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Request body
Fields
Returns
200
Draft auth link updated
Fields
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDAuth link not found
Verify the target identifier and that its endpoint or plugin route still exists.
409 CONFLICTThe auth link is not a DRAFT
Check the device’s current mode or state. Retry only after the conflict is resolved.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current auth link version; re-read the link and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing, or is the wildcard *; send the etag of the draft you last read
Inspect the returned code and family and follow the error handling guide.
/{namespace}/{principalType}/{principalId}/auth-links/{authLinkId}/disconnectSigns a customer-owned OAuth auth link out of its provider while keeping the OAuth app the customer registered. The provider tokens are deleted, the link's routes and the endpoints that exist only for it are removed, and the link returns to DRAFT with its OAuth client ID, client secret and provider configuration intact. The customer can authorize it again without entering them. Port OS does not call a provider revocation endpoint: the tokens are dropped from Port OS only.
The link must be CONNECTED, must use a customer-owned OAuth authorization variant and must hold its own OAUTH_CLIENT secret. A link that uses the Port OS app (portos-oauth), or any other variant, has no saved app configuration to keep: it is refused with AUTH_LINK_NOT_DISCONNECTABLE and is removed with deleteAuthLink. A link that is already a DRAFT is a 409 CONFLICT. The If-Match etag must match the link's current version; the wildcard If-Match: * is not accepted. The response carries the new ETag.
Required scope
Any authenticated access token
Header parameters
- If-Match
stringRequired The quoted strong entity tag of the resource the client last read (its `etag` field or `ETag` response header). The mutation is applied only when it still matches: a stale value returns 412 PRECONDITION_FAILED and a missing header returns 428 PRECONDITION_REQUIRED. `*` matches any current version of an existing group (RFC 9110), for a deliberate last-writer-wins change.
minLength: 1
Returns
200
The auth link is disconnected and is a DRAFT again, with its app configuration kept
Fields
Response headers
- ETag
string Opaque strong entity tag of the returned resource version. Send it back in `If-Match` on the next mutation of the same resource.
Errors
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDAuth link not found
Verify the target identifier and that its endpoint or plugin route still exists.
409 AUTH_LINK_NOT_DISCONNECTABLEThe auth link is already a DRAFT (CONFLICT), or it keeps no app configuration of the customer (AUTH_LINK_NOT_DISCONNECTABLE); delete it instead
Check the device’s current mode or state. Retry only after the conflict is resolved.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current auth link version; re-read the link and retry
Inspect the returned code and family and follow the error handling guide.
428 PRECONDITION_REQUIREDThe If-Match header is missing, or is the wildcard *; send the etag of the link you last read
Inspect the returned code and family and follow the error handling guide.
/auth-links/refreshRefreshes the credentials associated with an auth link. For OAuth-based links this exchanges the refresh token for a new access token via the plugin's token endpoint. For API-key-based links the key is returned as-is. The auth link is updated with the refreshed credentials.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Token refreshed successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Testing
Testing APIs create simulated devices for development.
Endpoint Simulators lets customers simulate endpoints to test out their flows and integrations.
/endpoint-simulatorsCreates a new endpoint simulator or updates an existing one. When an id is provided in the request body, the simulator with that ID is updated. When no id is provided, a new simulator is created along with its associated endpoint, auth link, and route.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
415 BAD_REQUESTUnsupported request media type.
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpoint-simulatorsDeletes an endpoint simulator and its associated endpoint, auth link, and route.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Simulator deleted successfully
No response body.
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDSimulator not found
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/endpoint-simulatorsLists endpoint simulators for the authenticated user. Supports pagination via maxResults and nextToken query parameters. When owner is not specified, defaults to the authenticated caller.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
Errors
400 BAD_REQUESTInvalid request parameters
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
403 FORBIDDENForbidden - insufficient permissions
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Tokens and OAuth
Token and OAuth APIs get and use access tokens.
Use tokens when you already have credentials and need an access token, or when you want to manage the long-lived tokens you hand to a CLI, device automation or agent.
/auth/tokenThis API is used to generate OAuth2 tokens.
The current OAuth token endpoint supports:
- Refresh token grants.
- Authorization code token grants.
Public DCR/CIMD clients exchange authorization_code with client_id, redirect_uri, code_verifier and the same resource as the consent request. No client_secret is issued. Refresh these grants with grant_type refresh_token, client_id, rotating refresh_token and the original resource. Store the replacement securely.
Required scope
Not required
Request body
Fields
- grant_type
stringRequired The OAuth grant type. Use authorization_code for code exchange, or refresh_token to exchange a refresh token for an access token.
- client_secret
string Required for confidential clients. Public DCR/CIMD clients omit this field and use PKCE.
- subject_token
string Identity token to exchange; required for urn:ietf:params:oauth:grant-type:token-exchange.
- subject_token_type
string Subject token type; use urn:ietf:params:oauth:token-type:id_token for identity token exchange.
- resource
string Exact resource from authorization; canonical issuer for REST or issuer/mcp for MCP. Required for DCR and CIMD.
format: "uri"
Returns
200
Token generated successfully
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/auth/revokePossession of the matching refresh token proves revocation authority. The endpoint invalidates the grant family; it does not delete the client or other users' grants.
Required scope
Not required
Returns
200
Grant revoked or token was already invalid.
No response body.
Errors
400Invalid request or client mismatch.
Check the request fields and any validation details, then correct the payload.
503Revocation storage unavailable; do not claim success.
Wait for the provider or bridge to recover, then retry safely with backoff.
/tokensLists tokens owned by the authenticated principal. A principal may only list tokens they own.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
Errors
400 BAD_REQUESTBad request
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized
Obtain a valid access token, then retry.
403 INSUFFICIENT_PERMISSIONSForbidden - not allowed to list tokens
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/tokensCreates an expiring, revocable bearer access key for the caller. The raw JWT is returned only once. Use purpose WEBSITE_LOGIN for full-account website sign-in and explicit OAuth consent, or API for automation (the default). Subject must be ~self or the authenticated principal's own identifier. Website keys cannot have restricted scopes, expire within seven days and require an existing Google account session to create. Vended and delegated credentials cannot create independent replacement keys. Website keys cannot be refreshed or exchanged for identity sessions. OAuth connections approved with a website key are independently revocable.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
- purpose
string API keys authenticate automation. WEBSITE_LOGIN keys grant full account website sign-in and explicit OAuth consent, cannot have restricted scopes, and must be created through an existing account sign-in. Keys expire and can be revoked; OAuth connections approved with a website key are revoked separately.
Default:
APIAllowed values: API, WEBSITE_LOGIN.
- subject
string Identity for the token. Use ~self for the current user (resolved to the authenticated principal at create time), or that same principal's full path. Other principals are rejected.
- expires_in
integer Lifetime in seconds, default 3600. Website sign-in keys require a positive lifetime of at most 604800 (seven days).
Returns
200
Token created successfully. The raw JWT is returned only once.
Errors
400Invalid request (e.g. invalid subject, malformed body)
Check the request fields and any validation details, then correct the payload.
401Unauthorized
Obtain a valid access token, then retry.
403Forbidden - not allowed to create a token for the given subject
Check the linked account and its permissions for the device or resource.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/tokens/{tokenId}Revokes the access key's active record so subsequent website and API requests using that bearer are rejected. The token ID is the ID returned by create/list and carried in its JWT jti claim. Only the owner may revoke it. OAuth connections explicitly approved with a website key are independent grants and must be revoked separately.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
401Unauthorized
Obtain a valid access token, then retry.
403Forbidden - not owner of the token
Check the linked account and its permissions for the device or resource.
404Token id not found
Verify the target identifier and that its endpoint or plugin route still exists.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Use OAuth flows to sign a person or an agent in to Port OS and get an access token for the other APIs.
/.well-known/oauth-authorization-serverDiscover the canonical issuer and endpoints for the deployed environment. Public REST grants use issuer as resource; MCP grants use issuer/mcp. CIMD client IDs must be public HTTPS metadata URLs. Registration is anonymous; device access requires user consent.
Required scope
Not required
Returns
200
Authorization server metadata.
Fields
/.well-known/jwks.json/.well-known/oauth-protected-resourceReturns OAuth 2.0 Protected Resource Metadata as defined by RFC 9728. This endpoint is publicly accessible and does not require authentication. Responses are cached for one hour.
Required scope
Not required
/.well-known/oauth-protected-resource/mcpReturns OAuth 2.0 Protected Resource Metadata for the MCP endpoint as defined by RFC 9728. This endpoint is publicly accessible and does not require authentication. Responses are cached for one hour.
Required scope
Not required
/auth/device-authorizationThis API is used by your device to obtain authorization from the resource owner via a device code or QR code that your device can scan.
See the OAuth device authorization docs for more.
Uses the existing managed-client device flow. This endpoint is public, but validates its client and supported device grant; anonymous DCR clients support authorization_code and optional refresh_token, and cannot start the device flow.
Required scope
Not required
Returns
200
Device authorization initiated successfully
Fields
Errors
400Invalid request parameters
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/oauth/registerRFC 7591 anonymous public-client registration. No Google session is required. This registration gives no user or resource permissions. Public clients must use PKCE S256, resource-bound authorization_code flow and explicit user consent. Client name, software_id and client_uri are unverified claims, not proof of Muse or any other product. Untrusted software_statement is rejected. Remote callbacks require HTTPS. HTTP is allowed for localhost or literal loopback IPs only, and local callbacks cannot be mixed with remote callbacks. SHA-256 of canonical client_name, sorted redirect_uris, sorted grant_types, sorted response_types and token_endpoint_auth_method identifies a registration. A repeated fingerprint returns the original client_id and accepted metadata, including its original scopes; it never overwrites metadata or expands scopes. Reused registrations do not consume new-registration quota. Unknown metadata extensions are ignored as required by RFC 7591. The deployment enforces rolling registration and retained-client quotas.
Required scope
Not required
Request body
Fields
- redirect_uris
array<string>Required Exact callbacks must form either an entirely localhost/loopback set or an entirely remote HTTPS set. HTTP is allowed only for localhost or literal loopback IPs. Local and remote callbacks cannot be mixed; user information and fragments are forbidden.
- grant_types
array<string> authorization_code, optionally with refresh_token, each at most once. Omitted, the server registers authorization_code and refresh_token and returns them.
uniqueItems: true
Errors
400Invalid metadata, redirect URI, or unapproved software statement.
Check the request fields and any validation details, then correct the payload.
415Use application/json.
Inspect the returned code and family and follow the error handling guide.
429Registration quota reached. Wait before retrying; do not loop.
Wait for Retry-After or details.retryAfterSeconds when supplied; otherwise use backoff.
Response headers
Retry-After- See the source specification for this header.
503Registration storage unavailable.
Wait for the provider or bridge to recover, then retry safely with backoff.
/oauth/authorizeBrowser entry for registered DCR or CIMD clients. Redirects to the sign-in and consent UI. The client must start its registered callback listener first, generate random state and an S256 challenge, and wait for the user. Validate callback state and iss before exchanging the code. A CIMD client_id is an HTTPS, non-loopback URL of a metadata document that the server fetches (size-limited). The document must repeat the URL as client_id, name the client, list HTTPS redirect_uris that pass the server's redirect rules, and declare either public authentication (none) or private_key_jwt with an RS256 or ES256 signing algorithm and an HTTPS jwks_uri. The redirect_uri you send must be one of the listed redirect_uris. Any other metadata is rejected with invalid_client. There is no per-deployment allowlist of CIMD client URLs.
Required scope
Not required
Query parameters
Returns
302
Navigate to sign-in and pending consent.
No response body.
Response headers
- Location
string format: "uri"
Errors
400Invalid or unsupported OAuth request.
Check the request fields and any validation details, then correct the payload.
/oauth/authorizeThe website uses this endpoint after sign-in. Agents open the authorization URL and wait for the user; they never manufacture the user session.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
400Invalid, expired or consumed request.
Check the request fields and any validation details, then correct the payload.
401User sign-in required.
Obtain a valid access token, then retry.
/oauth/authorization-requestReturns server-stored consent details to the authenticated website. Descriptive client metadata is unverified. Agents use the public authorization endpoint and wait for the user; a delegated device token cannot read this session-only endpoint.
Required scope
Any authenticated access token
Errors
400Invalid, expired or consumed request.
Check the request fields and any validation details, then correct the payload.
401User sign-in required.
Obtain a valid access token, then retry.
/oauth/authorization-responseNavigate the browser here using the continuation returned by authenticated consent. The opaque reference expires after two minutes and is consumed once. The server chooses the stored callback; caller-supplied redirect destinations are never used. Approval carries code, original state and issuer; denial carries access_denied, original state and issuer. No bearer token is needed.
Required scope
Not required
Returns
302
Navigate with GET to the exact registered callback.
No response body.
Response headers
- Location
string format: "uri"
Response headers
- Cache-Control
string no-store
Response headers
- Referrer-Policy
string no-referrer
Errors
400Missing, wrong-stage, expired or consumed continuation reference.
Check the request fields and any validation details, then correct the payload.
Identity and access
Identity and access APIs manage principals, clients, secrets and permissions.
Use principals when you manage who can act in Port OS.
/principalsLists principals owned by the authenticated caller by default. Supplying an owner selects that owner's principals, subject to the caller's principal-read permissions. Supplying an ID selects a single principal, subject to the same permission check. The default page size is 10 and the maximum page size is 100.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
- owner
string Owner whose principals to list. Defaults to the authenticated caller when ID is not supplied. A different owner requires principal-read permission for each returned principal.
- id
string Exact principal identifier to retrieve. The result is included only when the caller has principal-read permission.
- expand
array<string> Expansion tokens that request related or computed fields for each result. When omitted, the response contains only the default, shallower set of fields for each result.
Errors
400 BAD_REQUESTInvalid owner, ID, page size, or pagination token
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication is required
Obtain a valid access token, then retry.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/principalsCreates a new principal
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}Deletes a principal
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Principal deleted successfully
No response body.
Errors
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Use secrets to store a credential that Port OS needs to keep for you, such as a key a plugin uses to call a provider.
/secretsLists all secrets
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
Errors
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/secretsCreate a secret, which is a special type of data that is secured. Secrets are special data that can be very dangerous to use, and so is put in a special database.
Some well known types of secrets include: Random secret data, access tokens, passwords, and various others.
Generic secrets
If you want to keep something secret, and have it super safe, you can create a secret. This secret is stored in portos with a unique resource identifier for a storage document.
For example, maybe you have client credentials or something that is a generic json blob.
i.e. https://api.portoperatingsystem.com/storage/secrets/portos1.secret.<UUID>
Tokens
A specialized type of secret is a OAUTH token secret. This secret is often used to access plugins, so there are specialized indexes to make token look up faster.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/secrets/{secretId}Deletes a secret
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Secret deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Use clients when you build an application that signs in to Port OS with OAuth and need to register it, rotate its secrets, or remove it.
/oauth-clientsLists all OAuth clients for the authenticated user
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/oauth-clientsCreates a new OAuth client registration. This endpoint allows you to register a new OAuth client with Port OS. The client will be assigned a unique client ID and a client secret that can be used for OAuth authentication flows. The client secret is only returned once during creation, so make sure to store it securely.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
- clientName
stringRequired A human-readable string name of the client to be presented to the end-user during authorization. If omitted, the authorization server may display the raw "client_id" value to the end-user instead. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- clientUri
string A URL string of a web page providing information about the client. If present, the server should display this URL to the end-user in a clickable fashion. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- clientUriLocalized
object Localized versions of the client URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- logoUri
string A URL string that references a logo for the client. If present, the server should display this image to the end-user during approval. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- logoUriLocalized
object Localized versions of the logo URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized logo URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- redirectUris
array<string> An array of redirection URI strings for use in redirect-based flows such as the authorization code and implicit flows. Clients using these flows must register their redirection URI values. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- allowedOrigins
array<string> An array of authorized JavaScript origin strings. When set, the server validates the Origin header on token requests against this list. Origins are scheme + host (+ optional port), with no path.
- grantTypes
array<string> An array of OAuth 2.0 grant type strings that the client can use at the token endpoint, each at most once. The token endpoint refuses any other grant with `unauthorized_client`, and a refresh token is issued only to a client that registered refresh_token. authorization_code requires redirect_uris; client_credentials requires a client_secret_post, client_secret_basic or private_key_jwt client. When omitted, the server registers authorization_code and refresh_token, or client_credentials for a private_key_jwt client, and returns the registered list (RFC 7591 lets the server substitute values). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
uniqueItems: true
Default:
["authorization_code","refresh_token"]- responseTypes
array<string> An array of OAuth 2.0 response type strings that the client can use at the authorization endpoint. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tokenEndpointAuthMethod
string A string indicating the requested authentication method for the token endpoint. Values defined by this specification are "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", and "none". See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Default:
client_secret_basicAllowed values: client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, none.
- tokenEndpointAuthSigningAlg
string A string containing a JWS signing algorithm (alg value) that MUST be used for signing the JWT used to authenticate the client at the token endpoint for the "private_key_jwt" and "client_secret_jwt" authentication methods. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Allowed values: RS256, ES256.
- scopes
array<OAuthScope> List of OAuth scopes that this client can request. This is the primary field for specifying scopes. Scopes are mapped to Cedar permissions (e.g., "endpoint:read" maps to "endpoint.read"). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- scope
string A string containing a space-separated list of scope values that the client can use when requesting access tokens. This is a convenience field that represents the scopes array as a space-separated string. If both scope and scopes are provided, scopes takes precedence. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- contacts
array<string> An array of strings representing ways to contact people responsible for this client, typically email addresses. The authorization server may make these contact addresses available to end-users for support requests for the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tosUri
string A URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- tosUriLocalized
object Localized versions of the terms of service URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized terms of service URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- policyUri
string A URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- policyUriLocalized
object Localized versions of the privacy policy URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized privacy policy URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- jwksUri
string A URL string referencing the client's JSON Web Key (JWK) Set document, which contains the client's public keys. These keys can be used by higher-level protocols that use signing or encryption. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- jwks
OAuthClientJwks The client's JSON Web Key Set document value, which contains the client's public keys. This parameter is intended to be used by clients that cannot use the "jwks_uri" parameter, such as native clients that cannot host public URLs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Returns
200
OAuth client created successfully
Fields
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
401Unauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}Retrieves details of a specific OAuth client
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
OAuth client retrieved successfully
Fields
- clientName
stringRequired A human-readable string name of the client to be presented to the end-user during authorization. If omitted, the authorization server may display the raw "client_id" value to the end-user instead. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- clientUri
string A URL string of a web page providing information about the client. If present, the server should display this URL to the end-user in a clickable fashion. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- logoUri
string A URL string that references a logo for the client. If present, the server should display this image to the end-user during approval. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- redirectUris
array<string> An array of redirection URI strings for use in redirect-based flows such as the authorization code and implicit flows. Clients using these flows must register their redirection URI values. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- allowedOrigins
array<string> An array of authorized JavaScript origin strings. When set, the server validates the Origin header on token requests against this list. Origins are scheme + host (+ optional port), with no path.
- grantTypes
array<string> An array of OAuth 2.0 grant type strings that the client can use at the token endpoint. The token endpoint refuses any other grant with `unauthorized_client`, and a refresh token is issued only to a client that registered refresh_token. Registration stores authorization_code and refresh_token (client_credentials for a private_key_jwt client) when the request omits grant types; a stored client with an empty list may use only authorization_code (the RFC 7591 default). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- responseTypes
array<string> An array of OAuth 2.0 response type strings that the client can use at the authorization endpoint. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tokenEndpointAuthMethod
string A string indicating the requested authentication method for the token endpoint. Values defined by this specification are "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", and "none". See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Allowed values: client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, none.
- tokenEndpointAuthSigningAlg
string A string containing a JWS signing algorithm (alg value) that MUST be used for signing the JWT used to authenticate the client at the token endpoint for the "private_key_jwt" and "client_secret_jwt" authentication methods. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Allowed values: RS256, ES256.
- scope
string A string containing a space-separated list of scope values that the client can use when requesting access tokens. Valid scopes are: endpoint:read, endpoint:write, message:read, message:send, secret:read, secret:write, principal:read, principal:write, subscription:read, subscription:write, access-control-rule:read, access-control-rule:write. Scopes are mapped to Cedar permissions (e.g., "endpoint:read" maps to "endpoint.read"). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- scopes
array<OAuthScope> List of OAuth scopes that this client can request (array representation of scope). This is a convenience field that represents the space-separated scope string as an array. Scopes are mapped to Cedar permissions (e.g., "endpoint:read" maps to "endpoint.read").
- contacts
array<string> An array of strings representing ways to contact people responsible for this client, typically email addresses. The authorization server may make these contact addresses available to end-users for support requests for the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tosUri
string A URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- tosUriLocalized
object Localized versions of the terms of service URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized terms of service URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- policyUri
string A URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- policyUriLocalized
object Localized versions of the privacy policy URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized privacy policy URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- clientSecret
string The client secret string known only to the client and the authorization server. This secret is used for authenticating the client to the authorization server. This field is typically only returned during client registration and should not be included in GET responses for security reasons. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- clientSecretExpiresAt
integer An integer indicating the expiration time of the client secret, represented as the number of seconds since the Unix epoch (1970-01-01T00:00:00Z). A value of 0 indicates that the secret does not expire. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- jwksUri
string A URL string referencing the client's JSON Web Key (JWK) Set document, which contains the client's public keys. These keys can be used by higher-level protocols that use signing or encryption. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- jwks
OAuthClientJwks The client's JSON Web Key Set document value, which contains the client's public keys. This parameter is intended to be used by clients that cannot use the "jwks_uri" parameter, such as native clients that cannot host public URLs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- softwareId
string A unique identifier string assigned by the client developer or software publisher used by registration endpoints to identify the client software to be dynamically registered. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- softwareVersion
string A version identifier string for the client software identified by "software_id". The value of the "software_version" should change on any update to the client software identified by the same "software_id". See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Errors
400 BAD_REQUESTInvalid request payload
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
404 INVALID_VALUEOAuth client not found
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}Updates an existing OAuth client. This endpoint allows you to update the configuration of an OAuth client, such as its name, redirect URIs, grant types, and scopes. Note: The client secret cannot be updated through this endpoint. To rotate a secret, generate an additional secret through the client's secrets child collection and revoke the old secret after migrating consumers.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
- clientName
string A human-readable string name of the client to be presented to the end-user during authorization. If omitted, the authorization server may display the raw "client_id" value to the end-user instead. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- clientUri
string A URL string of a web page providing information about the client. If present, the server should display this URL to the end-user in a clickable fashion. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- clientUriLocalized
object Localized versions of the client URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- logoUri
string A URL string that references a logo for the client. If present, the server should display this image to the end-user during approval. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- logoUriLocalized
object Localized versions of the logo URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized logo URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- redirectUris
array<string> An array of redirection URI strings for use in redirect-based flows such as the authorization code and implicit flows. Clients using these flows must register their redirection URI values. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- allowedOrigins
array<string> An array of authorized JavaScript origin strings. When set, the server validates the Origin header on token requests against this list. Origins are scheme + host (+ optional port), with no path.
- grantTypes
array<string> An array of OAuth 2.0 grant type strings that the client can use at the token endpoint. The token endpoint refuses any other grant with `unauthorized_client`; a client with an empty list may use only authorization_code (the RFC 7591 default). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
uniqueItems: true
- responseTypes
array<string> An array of OAuth 2.0 response type strings that the client can use at the authorization endpoint. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tokenEndpointAuthMethod
string A string indicating the requested authentication method for the token endpoint. Values defined by this specification are "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", and "none". See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Allowed values: client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, none.
- tokenEndpointAuthSigningAlg
string A string containing a JWS signing algorithm (alg value) that MUST be used for signing the JWT used to authenticate the client at the token endpoint for the "private_key_jwt" and "client_secret_jwt" authentication methods. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Allowed values: RS256, ES256.
- scopes
array<OAuthScope> List of OAuth scopes that this client can request. This is the primary field for specifying scopes. Scopes are mapped to Cedar permissions (e.g., "endpoint:read" maps to "endpoint.read"). See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- scope
string A string containing a space-separated list of scope values that the client can use when requesting access tokens. This is a convenience field that represents the scopes array as a space-separated string. If both scope and scopes are provided, scopes takes precedence. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- contacts
array<string> An array of strings representing ways to contact people responsible for this client, typically email addresses. The authorization server may make these contact addresses available to end-users for support requests for the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
- tosUri
string A URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- tosUriLocalized
object Localized versions of the terms of service URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized terms of service URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- policyUri
string A URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- policyUriLocalized
object Localized versions of the privacy policy URI using language tags as keys (BCP 47 format). The keys are language tags and values are the localized privacy policy URIs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2.2
- jwksUri
string A URL string referencing the client's JSON Web Key (JWK) Set document, which contains the client's public keys. These keys can be used by higher-level protocols that use signing or encryption. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
format: "uri"
- jwks
OAuthClientJwks The client's JSON Web Key Set document value, which contains the client's public keys. This parameter is intended to be used by clients that cannot use the "jwks_uri" parameter, such as native clients that cannot host public URLs. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
401Unauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
404OAuth client not found
Verify the target identifier and that its endpoint or plugin route still exists.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}Deletes an OAuth client. This permanently removes the OAuth client and invalidates all associated tokens. This action cannot be undone.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
OAuth client deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
401Unauthorized - invalid or missing authentication token
Obtain a valid access token, then retry.
404OAuth client not found
Verify the target identifier and that its endpoint or plugin route still exists.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}/secretsLists active masked secrets for the selected OAuth client. Plaintext and hashes are never returned.
Required scope
Any authenticated access token
Errors
400 BAD_REQUESTInvalid request path.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication required.
Obtain a valid access token, then retry.
403 FORBIDDENThe caller cannot read or manage the selected OAuth client.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDOAuth client or secret not found.
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}/secretsCreates an additional secret and returns its plaintext once. Existing secrets remain active until explicitly revoked.
Required scope
Any authenticated access token
Errors
400 BAD_REQUESTInvalid request path.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication required.
Obtain a valid access token, then retry.
403 FORBIDDENThe caller cannot read or manage the selected OAuth client.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDOAuth client or secret not found.
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/port1/{principalType}/{principalId}/oauth-clients/{clientId}/secrets/{secretId}Permanently revokes one secret belonging to the selected OAuth client. Other secrets remain active.
Required scope
Any authenticated access token
Returns
200
Revoke OAuth Client Secret response.
No response body.
Errors
400 BAD_REQUESTInvalid request path.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication required.
Obtain a valid access token, then retry.
403 FORBIDDENThe caller cannot read or manage the selected OAuth client.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDOAuth client or secret not found.
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal server error.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Use access control to decide who can do what with a resource.
/access-control-rulesUpsert an access control rule.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
- operation
stringRequired The operation to grant access to. This could be a read, write, delete, etc. Please see the access controls list specification for all operations and their descriptions.
- resource
stringRequired The resource to grant access to. This could be an endpoint, a flow, a secret, etc. You can define this as either a class of resources, or a specific resource. Examples: - port1/principals/alice/endpoints/thermostat-zero grants access to that thermostat - port1/principals/alice/flows/* grants access to all flows owned by alice
- identity
stringRequired The identity to grant access to. This could be a user, a role, a group, etc. You can define this as either a class of identities, or a specific identity. Examples: - port1/principals/bob grants access to bob - * just grants access to all identities
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current access control rule; re-read the rule and retry
Inspect the returned code and family and follow the error handling guide.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/access-control-rulesGet access-control rules owned by the authenticated caller by default. Supplying an owner selects that owner's rules, and an ID selects one rule; results remain subject to the caller's access-control-rule-read permissions. The default page size is 10 and the maximum page size is 100.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Query parameters
- owner
string Owner whose access-control rules to get. Defaults to the authenticated caller unless an ID is supplied. A different owner requires read permission for each returned rule.
- id
string Exact access-control rule identifier to retrieve. The result is included only when the caller has read permission.
- nextToken
string Opaque token for the next page of results. Pass the continuation token returned by the previous response, unchanged. Omit it to start from the first page.
Errors
400 BAD_REQUESTInvalid owner, ID, page size, or pagination token
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication is required
Obtain a valid access token, then retry.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/access-control-rules/auditRead the audit of access-control rule changes, newest first. Every rule upsert and delete records who made it, through which client, why, and under which request, plus the rule as it was written. Entries hold identifiers and the rule's shape only, never token text. Entries older than the configured retention (auth.audit.retentionDays, default 365) are pruned at server start.
Only operator callers (operational tokens) and system zero may read the audit; any other caller receives 403. The default page size is 50 and the maximum is 200.
Required scope
Any authenticated access token
Query parameters
Errors
400 BAD_REQUESTInvalid filter, page size, time or pagination token
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication is required
Obtain a valid access token, then retry.
403 FORBIDDENThe caller is not an operator
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/access-control-rules/evaluateEvaluate an access control rule.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Request body
Fields
Returns
200
Access control rule evaluated successfully
Fields
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/access-control-rules/{ruleId}Delete an access control rule.
Requires a signed user/session or the existing authenticated client flow. The anonymous DCR delegated device profile cannot invoke this operation.
Required scope
Any authenticated access token
Returns
200
Access control rule deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
412 PRECONDITION_FAILEDThe If-Match etag does not match the current access control rule; re-read the rule and retry
Inspect the returned code and family and follow the error handling guide.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
Automations
Automations APIs are used to automate devices and other things.
When a message gets sent to port OS, you may want to listen to it.
/subscriptionsLists subscriptions owned by the authenticated caller when owner is omitted. Supplying owner selects that owner's subscriptions, filtered by the caller's SubscriptionRead permissions. Supplying id selects the exact subscription and still applies its read permission. The default page size is 10 and the maximum is 100; results use nextToken pagination.
Required scope
subscription:readQuery parameters
- owner
string Filter the results by the owner of the resource. Defaults to the authenticated caller's resources when omitted. Use '~caller' to make that scope explicit. Another owner can be selected only for resources the caller can read.
- expand
array<string> Expansion tokens that request related or computed fields for each result. When omitted, the response contains only the default, shallower set of fields for each result.
Errors
400 BAD_REQUESTInvalid query parameter.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDUnauthorized — invalid or missing authentication token.
Obtain a valid access token, then retry.
403 INSUFFICIENT_PERMISSIONSForbidden — insufficient permissions for the requested owner.
Check the linked account and its permissions for the device or resource.
500 INTERNALInternal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/subscriptionsUsed to upsert a subscription to an event stream.
Required scope
subscription:writeErrors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/subscriptions/{subscriptionId}Delete a subscription.
Required scope
subscription:writeReturns
200
Subscription deleted successfully
No response body.
Errors
400Invalid request payload
Check the request fields and any validation details, then correct the payload.
500Internal server error
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/subscriptions/{subscriptionId}Returns the authorized canonical subscription. The verified MCP target is a same-ID binding reference; callback credentials and URL are never included.
Required scope
subscription:readReturns
200
Inspect an authorized subscription
Fields
- query
QueryRequired Shared Port OS query AST modeled as a recursive JSON graph supporting match, comparison, pattern, freeform, and boolean operators.
- context
object Structured filter criteria for granular device event matching. Keys are dot-notation paths into the message (e.g., "header.namespace", "header.name"). Values are the expected values to match. All context entries must match for the subscription to fire. Omitting context matches all events.
Errors
400 BAD_REQUESTInvalid request; stable code identifies the validation failure.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication required.
Obtain a valid access token, then retry.
403 FORBIDDENResource authorization denied.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDResource inaccessible, missing or past retention.
Verify the target identifier and that its endpoint or plugin route still exists.
500 INTERNALInternal failure.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.
/{namespace}/{principalType}/{principalId}/subscriptions/{subscriptionId}Changes the label only. Preserves the ID, original host arguments, query, target, generation and activation sequence. Refresh and unsubscribe continue to resolve the original host identity.
Required scope
subscription:writeReturns
200
Edit an authorized subscription label
Fields
- query
QueryRequired Shared Port OS query AST modeled as a recursive JSON graph supporting match, comparison, pattern, freeform, and boolean operators.
- context
object Structured filter criteria for granular device event matching. Keys are dot-notation paths into the message (e.g., "header.namespace", "header.name"). Values are the expected values to match. All context entries must match for the subscription to fire. Omitting context matches all events.
Errors
400 BAD_REQUESTInvalid request; stable code identifies the validation failure.
Check the request fields and any validation details, then correct the payload.
401 UNAUTHORIZEDAuthentication required.
Obtain a valid access token, then retry.
403 FORBIDDENResource authorization denied.
Check the linked account and its permissions for the device or resource.
404 NOT_FOUNDResource inaccessible, missing or past retention.
Verify the target identifier and that its endpoint or plugin route still exists.
409 CONCURRENT_MODIFICATIONsubscription_identity_immutable: request attempts to change monitoring identity. Existing binding and queued jobs are untouched.
Check the device’s current mode or state. Retry only after the conflict is resolved.
500 INTERNALInternal failure.
Retry with backoff only when the operation is safe to repeat. Include the error code when reporting persistent failures.