Skip to main content

Identity Management

๐Ÿ“„๏ธ List gate servers (deduped)

Returns one row per distinct gate server (deduped by `analytics_correlation_id`, latest event per server). Backed by the shared `events_dataset.external_events_json` firehose today; a dedicated table is a follow-up. Default lookback when neither `start_time` nor `end_time` is provided is **7 days** (matches gate's heartbeat cadence so a fresh open reliably lists all live servers). If only one bound is provided, the other is derived using the 7-day default. Same 30-day cap applies when both bounds are provided (`end_time - start_time > 30d` is rejected with HTTP 400).

๐Ÿ“„๏ธ List handled gate requests

Returns one row per served gate request. Each row is the fully-hydrated `GateRequestHandled_v1` event as stored, including per-plugin timings and per-round-trip HTTP metadata inline (no separate detail endpoint). Default lookback when neither `start_time` nor `end_time` is provided is **24 hours** โ€” the endpoint is a high-volume activity log so the short default protects casual opens from scanning a full 30-day window. If only one bound is provided, the other is derived using the 24-hour default. Same 30-day cap applies when both bounds are provided.

๐Ÿ“„๏ธ List gate heartbeats (deduped)

Returns the newest heartbeat per distinct gate process โ€” same deduplication shape as `/gate/server-starts` (partition by `analytics_correlation_id`, newest `publish_time` wins). The row's `sequence` counter lets a caller distinguish 'gate has been alive throughout' (monotonic per process) from 'gate restarted' (new correlation_id with sequence starting at 1). Default lookback when neither `start_time` nor `end_time` is provided is **7 days** โ€” 'who is alive right now' framing. Same one-bound derivation and 30-day cap as the sibling endpoints.

๐Ÿ“„๏ธ Create or replace the org's MCP policy

Upserts the org's policy and records a new revision. Restricted to admins: this is the write that changes what gate enforces. The Rego is compiled before the write is accepted, whether it was generated from `rules` or authored by hand. This is the only place the author sees a compile error: OPA activates a bundle only if it compiles, so a broken policy served to gate leaves the PREVIOUSLY activated policy in force and the console would show the broken one as current. Sending `authored_by: rego` when the stored policy is `rules` walks the one-way door described on `GateMCPPolicyAuthoringMode`. The stored `rules` are retained, not cleared.

๐Ÿ“„๏ธ The org's policy as an OPA bundle

Serves the org's policy as a gzipped OPA bundle for gate's OPA plugin to fetch directly. Authenticated with the org API key gate already holds for telemetry, so enabling policy enforcement introduces no new credential to provision or rotate. No x-rbac-enabled: the caller is a gate process, not a console session. Responds 200 with `application/gzip` โ€” a tarball containing `/policy.rego` and `/.manifest` โ€” or 304 when the caller's `If-None-Match` matches the current revision's ETag. An org with no policy, or a policy with `enabled: false`, gets a valid permit-everything bundle rather than a 404: gate must always end up with a compilable bundle, and a fetch error would leave whatever policy it activated last in force indefinitely.

๐Ÿ“„๏ธ Resolve a caller's attributes and groups

Answers 'who is the principal behind this token' so a policy can decide on group membership and directory attributes rather than only on what the token happens to carry. One endpoint over BOTH identity planes, resolved in order: a SlashID person by id, then an identity in the NHI graph by connection subject, then โ€” only as a fallback โ€” a UNIQUE email-claim match. An ambiguous email resolves to nothing, because an authorization decision must never rest on a guessed identity. No x-rbac-enabled: the caller is a gate process, not a console session. Authenticated with the same org API key gate already holds, so enabling attribute conditions provisions no new credential. A caller that matches nothing is a 200 with `resolved: false`, not a 404: 'we looked and found nobody' is an answer, and gate caches it briefly so an unknown subject cannot hammer this endpoint. A 404 would be indistinguishable from the endpoint being unreachable, which is the case gate must handle differently.

๐Ÿ“„๏ธ Distinct values seen in recent traffic, for builder typeahead

Enumerates the values this org's own MCP traffic actually carried, so a rule is authored against real tool names instead of remembered ones. A policy keyed on a misremembered tool name compiles, activates, and silently never fires โ€” which is exactly the failure this exists to make impossible to reach by typing. Tool values are read from the recorded `mcp.tools`, never the scalar `mcp.tool`, which is empty for batched payloads by design: a scalar-keyed aggregate would omit precisely the batched calls an operator is hunting for. NOT paginated, and deliberately so. This is a suggestion list, not a listing: it answers with the `limit` most frequently observed values matching `prefix`, ordered by frequency. Walking pages of it would be walking the tail โ€” values seen once or twice โ€” which is not what a typeahead is for, and the underlying store is an aggregate over an analytics table where a keyset cursor would have to be minted against a GROUP BY result rather than an index. Narrowing with `prefix` is how a caller reaches a value outside the head, and it is what a typeahead does anyway.

๐Ÿ“„๏ธ Replay recent traffic against a draft policy

Compiles the draft, replays this org's recently recorded requests through it, and reports what would change. Nothing is saved and nothing gate enforces is touched. 'Would this rule have blocked anything real?' is the question an operator actually has before saving, and it is not answerable from the rule text. It IS answerable from traffic that already happened. Every replayed request lands in one of THREE buckets, not two: allowed, denied, or unevaluable. Recorded telemetry is lossy by design โ€” `mcp_visibility` suppresses fields, and a recorded batch may carry no per-call pairing โ€” so some requests cannot be scored. Counting those as 'would not have matched' would manufacture confidence about exactly the rules the operator is least sure of.

๐Ÿ“„๏ธ Mint a token for a person

This endpoint creates a token for a specific user. Custom claims can be specified in the request body which will be added to the token's payload. Custom claims are added to the token's payload. Tokens created with this endpoint will have an `authenticated_methods` claim equal to ['api']. The following claims are reserved and cannot be specified: - aud - exp - jti - iat - iss - nbf - sub - prev_token_id - oid - org_id - user_id - person_id - first_token - authenticated_methods - oidc_tokens - user_token - groups - roles - access_token - refresh_token - id - id_token - gdpr - gdpr_consent - gdpr_consent_level - parent_user_id - parent_person_id - parent_org_id - parent_oid - attributes - custom_claims - slashid - slashid.dev - slashid.com - slashid.me - sid With the following request body: ``` { 'custom_claims': { 'foo': 'bar', 'baz': {'everything': 42} } } ``` the token in the response will have the following payload: ``` { 'authenticated_methods': [ 'api' ], 'baz': { 'everything': 42 }, 'exp': <expiration time timestamp>, 'first_token': false, 'foo': 'bar', 'iat': <issued at timestamp>, 'iss': <token issuer>, 'jti': <token ID>, 'oid': <organisation ID>, 'person_id': <person ID> } ```

๐Ÿ“„๏ธ Create or modify attributes for a person in multiple buckets

Create new attributes or modify existing attributes for a person in one or more attribute buckets. The request body should be a nested object, with bucket names as top-level keys, and key-value pair attributes as values, as shown in the example request body. The buckets must exist and must be accessible by the organization identified in the request header. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Any existing attribute that isn't referenced by key in the request is left untouched.

๐Ÿ“„๏ธ Create or modify attributes for a person in multiple buckets

Create new attributes or modify existing attributes for a person in one or more attribute buckets. The request body should be a nested object, with bucket names as top-level keys, and key-value pair attributes as values, as shown in the example request body. The buckets must exist and must be accessible by the organization identified in the request header. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Replaces the set of existing attributes with those present in the request. In other words, it deletes any existing attributes that aren't referenced by key in the request.

๐Ÿ“„๏ธ Delete attributes for a person from a single bucket

Delete attributes for a person from a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. Attribute names can be provided as a comma-separated list in the `attributes` query parameter. Empty attribute names are not permitted. If no attribute names are specified, all attributes in the bucket will be deleted. WARNING: this action is permanent and cannot be undone.

๐Ÿ“„๏ธ Create or modify attributes for a person in a single bucket

Create new attributes or modify existing attributes for a person in a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. The request body should be an object consisting of key-value pair attributes. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Any existing attribute that isn't referenced by key in the request is left untouched.

๐Ÿ“„๏ธ Create or modify attributes for a person in a single bucket

Create new attributes or modify existing attributes for a person in a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. The request body should be an object consisting of key-value pair attributes. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Replaces the set of existing attributes with those present in the request. In other words, it deletes any existing attributes that aren't referenced by key in the request.

๐Ÿ“„๏ธ Set the groups for a person

Add the person to the groups specified in the request body, and remove the person from any other existing groups. All groups in the request body must already exist. Duplicate groups in the request body will be ignored. If an empty list is supplied in the request body, the person will be removed from all groups they are currently a member of, and will not be added to any others. New groups can be created with the [POST /groups](/docs/api/post-groups) endpoint.

๐Ÿ“„๏ธ Retrieve the list of person's organizations

Retrieve details of all the organizations a person belongs to, including: - The organization of the request: the person must be a member of the organization you authenticate with for you to be allowed to retrieve this list - Any sub- and super-organizations that share the person pool with the organization of the request: A hierarchy of organizations can be created using [this API endpoint](/docs/api/post-organizations-suborganizations). When organizations are configured to share a person pool, if the same person registers with multiple organizations in the pool using the same handle, all organizations will see the same person ID for that person.

๐Ÿ“„๏ธ Create new person

Create a new person linked to your organization. The request must contain at least one handle for the person (email address, phone number, or username). Optionally, you can specify a list of roles, a geographical region and attributes. Attributes are a map from attribute bucket names to key-value pairs. If a person with the specified handles already exists, an error will be returned. If the region is not specified, the person will be created in the region closest to the region of your backend. If you explicitly specify the region and a person with the provided handle already exists in a different region, this endpoint will return an error. A person's region association is eventually consistent between regions. Because of that, creating a person between regions within the time frame of data replication can result in a person being created in multiple regions. You can learn more on the [Cross-region replication model](/docs/access/concepts/replication) page.

๐Ÿ“„๏ธ Bulk import persons

Bulk import persons into your organization by uploading a CSV file. The import will take the following CSV column headers into consideration: 'slashid:emails','slashid:phone_numbers','slashid:usernames','slashid:region','slashid:groups','slashid:attributes' Each row in the CSV must contain at least one handle for the person (email address, phone number, or a username). Optionally, you can specify a list of roles, a geographical region and attributes. Attributes are a JSON-encoded map from attribute bucket names to key-value pairs. Email addresses, phone numbers, and usernames must be comma-separated.

๐Ÿ“„๏ธ Create a group

This endpoint creates a new persons group with the given name. If the group exists already, no action will be taken. The group name must be unique within your organization; is case-sensitive; and must conform to the following: - must be at least 2 characters long - may be at most 100 characters long - may contain only the characters `A-Z a-z 0-9 - _ .` - must start and end with an alphanumeric character (`A-Z a-z 0-9`) A person can be added to a group through the [`POST /persons/:person_id/groups`](/docs/api/put-persons-person-id-groups) endpoint.

๐Ÿ“„๏ธ Create a permission

This endpoint creates a new permission with the given name. If the permission exists already, no action will be taken. A person can be assigned a permission through the [`POST /persons/:person_id/permissions`](/docs/api/put-persons-person-id-permissions) endpoint. Sub-organization can create permissions only when [`inherit_rbac_pools` is disabled](/docs/api/post-organizations-suborganizations). If `inherit_rbac_pools` is enabled, the permissions from the root organization will be inherited.

๐Ÿ“„๏ธ Remove an external credential by id

Remove the specified external credential by id or delete all external credentials. If a credential id is specified, it takes precedence over the deleteAll parameter, so a request with a credentialId specified and deleteAll=true will delete only the specified credential from the list of external credentials. If the specified credential id does not exist in the list of allowed origin credentials, no action will be taken, and a 200 status code will be returned.

๐Ÿ“„๏ธ Modify the named template

Sets the named message template for your organization. If a template already exists it will be overwritten and cannot be retrieved. The following templates are available for customization: - `email_authn_challenge`: E-mail template with magic link; - `text_authn_challenge`: SMS template with magic link; - `text_otp_message`: SMS template with OTP code; - `email_password_reset_challenge`: E-mail template with a password reset link; - `text_password_reset_challenge`: SMS template with a password reset link. All templates must conform to the [mustache template syntax](https://mustache.github.io/mustache.5.html). The following tags are available: - `{{attestation_url}}`: Magic link URL (at least one of `attestation_url` and `attestation_query_string` are required on `email_authn_challenge` and `text_authn_challenge`) - `{{attestation_query_string}}`: Magic link URL query string (at least one of `attestation_query_string` and `attestation_url` are required on `email_authn_challenge` and `text_authn_challenge`) - `{{password_reset_url}}`: Password reset URL (required on `email_password_reset_challenge` and `text_password_reset_challenge`) - `{{otp_code}}`: OTP code (Required on `text_otp_message`) - `{{org_name}}`: Organization name (Optional) - `{{new_user}}`: `true` for new signups, false for returning users (Optional) Constraints: - Email templates: - Sender name must be at most 128B in size - Subject must be at most 128B in size - Body must be HTML and at most 50KB in size. - SMS: - Body must be at most 140 bytes in size.

๐Ÿ“„๏ธ Add allowed domains

Add the specified domain patterns to the list of allowed origin domains, or overwrite the existing list. Domains may be specified exactly, or as patterns using asterisk wildcards '*'. The '*' wildcard will match zero or more characters. If a pattern is used, any origin domain matching the pattern will be treated as an allowed domain. Otherwise, the origin domain must match the allowed domain exactly. Matching is case-insensitive. For example, to specify that domain 'xyz.com' and all of its subdomains are allowed, use ['xyz.com', '*.xyz.com']. This would allow requests with origins 'xyz.com', 'a.xyz.com', 'b.c.xyz.com', and so on (but would not allow requests from 'xyz.net'). If overwrite is set to true, all existing allowed domains/patterns will be removed and replaced with the provided list. If overwrite is set to false, the provided list of domain patterns will be appended to the existing list of allowed origin domains. Adding a domain pattern that already exists in the list of allowed origin domains will have no effect.

๐Ÿ“„๏ธ Claim ownership of an email domain

Record a pending ownership claim for an email domain and return the DNS TXT record that proves ownership (`txt_record`). Publish that record; verification is currently performed by SlashID (a scheduled DNS check will follow). A verified domain routes Console logins for `<anything>@<domain>` straight to this organization's configured identity provider (one-step SSO). Only organizations managed through the Console can claim domains. Claiming a domain that is already claimed by this organization returns the existing claim unchanged. The domain is stored in canonical form (lowercase, ASCII); public suffixes such as `com` or `co.uk` cannot be claimed.

๐Ÿ“„๏ธ Modify the organization's token template

Sets the token template for your organization. If a template already exists it will be overwritten and cannot be retrieved. The template must conform to the [mustache template syntax](https://mustache.github.io/mustache.5.html). The following tags are available: - `{{ organization }}`: provides information on the organization. Renders to ```json { 'id': '00000000-0000-0000-0000-000000000000', 'name': 'Your Org Name', 'ancestors': [ { 'id': '00000000-0000-0000-0000-111111111111', 'name': 'Parent Name' }, ... ] } ``` The following sub-tags are available: - `{{ organization.id }}`: the ID of the organization. Renders to `00000000-0000-0000-0000-000000000000` - `{{ organization.name }}`: the name of the organization. Renders to `Your Org Name` - `{{ organization.ancestors }}`: the list of ancestor organizations, starting from the parent up to the root. Renders to ```json [{'id': '00000000-0000-0000-0000-111111111111', 'name': 'Parent Name'}, ...] ``` - `{{ person.permissions }}`: provides information on the person's permissions (additional and from roles). Renders to ```json [ 'permission1', 'permission2' ] ```

๐Ÿ“„๏ธ Create or modify attributes for the organization in multiple buckets

Create new attributes or modify existing attributes for the organization in one or more attribute buckets. The request body should be a nested object, with bucket names as top-level keys, and key-value pair attributes as values, as shown in the example request body. The buckets must exist and must be accessible by the organization identified in the request header. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Any existing attribute that isn't referenced by key in the request is left untouched.

๐Ÿ“„๏ธ Create or modify attributes for the organization in multiple buckets

Create new attributes or modify existing attributes for the organization in one or more attribute buckets. The request body should be a nested object, with bucket names as top-level keys, and key-value pair attributes as values, as shown in the example request body. The buckets must exist and must be accessible by the organization identified in the request header. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Replaces the set of existing attributes with those present in the request. In other words, it deletes any existing attributes that aren't referenced by key in the request.

๐Ÿ“„๏ธ Delete attributes for the organization from a single bucket

Delete attributes for the organization from a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. Attribute names can be provided as a comma-separated list in the `attributes` query parameter. Empty attribute names are not permitted. If no attribute names are specified, all attributes in the bucket will be deleted. WARNING: this action is permanent and cannot be undone.

๐Ÿ“„๏ธ Create or modify attributes for the organization in a single bucket

Create new attributes or modify existing attributes for the organization in a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. The request body should be an object consisting of key-value pair attributes. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Any existing attribute that isn't referenced by key in the request is left untouched.

๐Ÿ“„๏ธ Create or modify attributes for the organization in a single bucket

Create new attributes or modify existing attributes for the organization in a single attribute bucket. The bucket must exist and must be accessible by the organization identified in the request header. The request body should be an object consisting of key-value pair attributes. Attribute names may be at most 70 bytes long. Attribute values must be JSON-serializable and are limited to 64KiB. Replaces the set of existing attributes with those present in the request. In other words, it deletes any existing attributes that aren't referenced by key in the request.

๐Ÿ“„๏ธ Create a new webhook

Create a new webhook for your organization. Webhooks will be called in response to triggers, and you can use the `/organizations/webhooks/{webhook_id}/triggers` endpoint to specify which trigger(s) should call each webhook. The request body must contain the target URL for the webhook, and this must be unique for your organization (you cannot have two webhooks with the same target URL). The target URL must use the HTTPS protocol, and must have a host. The host cannot be `localhost` or an IP address. The target URL may have a path element, but may not contain query parameters or fragments. Additionally, you can specify custom headers that will be sent in the HTTP header of each request to your webhook's target URL. These are encrypted when stored, so can be used for sensitive data. You can also choose the webhook's timeout value. This will determine how long your webhook server has to respond to a request before it is considered failed. If you do not set a value, defaults will be used: for synchronous webhook calls, this is 2 seconds; for asynchronous calls, 10 seconds. When choosing a timeout, you should consider how long your webhook will typically need to process a request, and how this may affect user experience (in the case of sychronous webhooks). The value provided should be a string that can be parsed as a [Golang `time.Duration`](https://pkg.go.dev/time#Duration); for example, '10s', '500ms'. The timeout may not be more than 20 seconds, and cannot be negative. If you do not set a timeout, when retrieving the webhook you will see a timeout set to 0s, indicating that the defaults will be used when the webhook is called. You may also specify a name and description for the webhook. The response will contain a webhook ID, which uniquely identifies the newly created webhook. This ID can be used to modify or delete the webhook, and to add triggers for it. For more information on using webhooks with SlashID, see our [dedicated guide](/docs/access/guides/webhooks).

๐Ÿ“„๏ธ Retrieve verification JWKS for webhooks

Endpoint to retrieve a JSON Web Key Set (JWKS) for webhook body verification. When SlashID calls your webhooks in response to a trigger, the body of the request is a signed and encoded JSON Web Token (JWT). The public part of the signing key can be retrieved from this endpoint and used to verify that the request body comes from SlashID, is intended for your webhook, and has not been modified. SlashID uses the ES256 JSON Web Algorithm (JWA), which is the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-256 curve and SHA256. Support for JWTs and JWKS is available for many languages - [jwt.io](https://jwt.io/libraries) maintains an extensive (but not exhaustive) list of libraries for popular languages. Note that this endpoint is rate-limited to 30 requests per minute per organization, with bursts of 60. It is therefore recommended that you cache the keyset locally in your webhook handlers.

๐Ÿ“„๏ธ Create a new OAuth2 client

Create a new client ID/secret pair that can be used with the client credentials grant type. A valid SlashID organization ID and API key must be used to authenticate and authorize the request. The client credentials can be configured. SlashID currently supports the following OAuth 2.0 grants: - client credentials - authorization code - refresh token The `grant_types` field may contain only the values `client_credentials` and `authorization_code`. Other values will return a `400` error code. The client credentials grant requires a client secret, and so all clients allowing that grant should be regarded as confidential, as defined in the OAuth 2.0 specification (https://oauth.net/2/client-types/). The response body will include the client ID and client secret. For the client credentials grant, these can be used with the `/oauth2/tokens` endpoints to obtain an access token. For the authorization code grant, these can be used as described in the [OAuth 2.0 specification](https://datatracker.ietf.org/doc/html/rfc6749). The client secret must be stored securely, and cannot be retrieved in subsequent API calls. If you lose the client secret, a new secret can be generated using the `PUT /oauth2/clients/{oauth_client_id}/secret` endpoint.

๐Ÿ“„๏ธ Mint OAuth 2.0 & OIDC tokens

This endpoint creates a set of OAuth 2.0 tokens for a specific user. To generate a refresh token request scope `offline_access`. To generate an ID token request scope `openid`. Custom claims can be specified in the request body which will be added to the token's payload. Custom claims are added to the token's payload. Tokens created with this endpoint will have an `authenticated_methods` claim equal to ['api']. The following claims are reserved and cannot be specified: - aud - exp - jti - iat - iss - nbf - sub - prev_token_id - oid - org_id - user_id - person_id - first_token - authenticated_methods - oidc_tokens - user_token - groups - roles - access_token - refresh_token - id - id_token - gdpr - gdpr_consent - gdpr_consent_level - parent_user_id - parent_person_id - parent_org_id - parent_oid - attributes - custom_claims - slashid - slashid.dev - slashid.com - slashid.me - sid With the following request body: ``` { 'custom_claims': { 'foo': 'bar', 'baz': {'everything': 42} } } ``` the token in the response will have the following payload: ``` { 'authenticated_methods': [ 'api' ], 'baz': { 'everything': 42 }, 'exp': <expiration time timestamp>, 'first_token': false, 'foo': 'bar', 'iat': <issued at timestamp>, 'iss': <token issuer>, 'jti': <token ID>, 'oid': <organisation ID>, 'person_id': <person ID> } ```