Skip to content
bucker

API reference

sso

SAML and OIDC single sign-on connections. 6 operations. Shapes are sketches of the declared schemas, bounded in depth — the authoritative document is linked from the index.

GET/orgs/{orgSlug}/sso/connections

Requires bearerAuth

Parameters

Parameters for GET /orgs/{orgSlug}/sso/connections
NameInTypeNotes
orgSlugrequiredpathstringmin length 1, max length 64

Response 200

{
  connections: {
    id: string
    orgId: string
    protocol: "SAML" | "OIDC"
    enabled: boolean
    enforced: boolean
    defaultRole: string
    attributeMapping: Record<string, unknown> | null
    roleMapping: Record<string, unknown> | null
    idpEntityId: string | null
    idpSsoUrl: string | null
    idpCertificateConfigured: boolean
    idpCertificateAlternateCount: number
    oidcIssuer: string | null
    oidcClientId: string | null
    oidcClientSecretConfigured: boolean
    oidcScopes: string[]
    createdAt: string
    updatedAt: string
  }[]
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 401 · `unauthorized` — no credential, or one that is expired, revoked or not valid for this resource. `mfa_required` when the credential is good but a second factor is owed.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 403 · `forbidden` — the credential is valid and its scopes or this principal’s membership do not reach this resource. Scopes are re-intersected with live memberships on every request, so this can appear for a token that worked yesterday.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 404 · `not_found` — no such resource, OR one this principal cannot see. The two are deliberately one answer: a 403 would confirm the existence of something whose identifier is guessable.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl https://api.bucker.io/orgs/{orgSlug}/sso/connections \
  -H 'authorization: Bearer $BUCKER_TOKEN'

DELETE/orgs/{orgSlug}/sso/connections/{protocol}

Requires bearerAuth

Parameters

Parameters for DELETE /orgs/{orgSlug}/sso/connections/{protocol}
NameInTypeNotes
orgSlugrequiredpathstringmin length 1, max length 64
protocolrequiredpath"SAML" | "OIDC"one of "SAML" | "OIDC"

Response 200

{
  deleted: true
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 401 · `unauthorized` — no credential, or one that is expired, revoked or not valid for this resource. `mfa_required` when the credential is good but a second factor is owed.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 403 · `forbidden` — the credential is valid and its scopes or this principal’s membership do not reach this resource. Scopes are re-intersected with live memberships on every request, so this can appear for a token that worked yesterday.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 404 · `not_found` — no such resource, OR one this principal cannot see. The two are deliberately one answer: a 403 would confirm the existence of something whose identifier is guessable.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl -X DELETE https://api.bucker.io/orgs/{orgSlug}/sso/connections/{protocol} \
  -H 'authorization: Bearer $BUCKER_TOKEN'

PUT/orgs/{orgSlug}/sso/connections/{protocol}

Requires bearerAuth

Parameters

Parameters for PUT /orgs/{orgSlug}/sso/connections/{protocol}
NameInTypeNotes
orgSlugrequiredpathstringmin length 1, max length 64
protocolrequiredpath"SAML" | "OIDC"one of "SAML" | "OIDC"

Request body (required) · application/json

{
  enabled?: boolean
  enforced?: boolean
  defaultRole?: "VIEWER" | "BILLING" | "MEMBER" | "ADMIN"
  attributeMapping?: Record<string, string | string[]> | null
  roleMapping?: Record<string, string> | null
  idpEntityId?: string | null
  idpSsoUrl?: string (uri) | null
  idpCertificate?: string | null
  idpCertificateAlternates?: string[]
  oidcIssuer?: string (uri) | null
  oidcClientId?: string | null
  oidcClientSecret?: string | null
  oidcScopes?: string[]
}

Response 200

{
  id: string
  orgId: string
  protocol: "SAML" | "OIDC"
  enabled: boolean
  enforced: boolean
  defaultRole: string
  attributeMapping: Record<string, unknown> | null
  roleMapping: Record<string, unknown> | null
  idpEntityId: string | null
  idpSsoUrl: string | null
  idpCertificateConfigured: boolean
  idpCertificateAlternateCount: number
  oidcIssuer: string | null
  oidcClientId: string | null
  oidcClientSecretConfigured: boolean
  oidcScopes: string[]
  createdAt: string
  updatedAt: string
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 401 · `unauthorized` — no credential, or one that is expired, revoked or not valid for this resource. `mfa_required` when the credential is good but a second factor is owed.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 403 · `forbidden` — the credential is valid and its scopes or this principal’s membership do not reach this resource. Scopes are re-intersected with live memberships on every request, so this can appear for a token that worked yesterday.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 404 · `not_found` — no such resource, OR one this principal cannot see. The two are deliberately one answer: a 403 would confirm the existence of something whose identifier is guessable.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl -X PUT https://api.bucker.io/orgs/{orgSlug}/sso/connections/{protocol} \
  -H 'authorization: Bearer $BUCKER_TOKEN' \
  -H 'content-type: application/json' \
  -d '{ … }'

POST/sso/handoff

No declared credential scheme

Reachable without a Bucker session token. Spends the one-time code the two legs above 302 to the dashboard with; the opaque 256-bit code in the body IS the credential, exactly as the refresh token is at /auth/refresh, and the session it buys does not exist until it is spent.

Request body (required) · application/json

{
  code: string
}

Response 200

{
  orgId: string
  role: string
  provisioned: boolean
  tokens: {
    accessToken: string
    refreshToken: string
    expiresIn: number
    tokenType: "Bearer"
  }
  user: {
    id: string
    email: string
    name: string
    avatarUrl: string | null
    locale: string
    theme: string
    emailVerified: boolean
    mfaEnabled: boolean
    createdAt: string
  }
  next: string | null
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl -X POST https://api.bucker.io/sso/handoff \
  -H 'content-type: application/json' \
  -d '{ … }'

GET/sso/oidc/{orgSlug}/callback

No declared credential scheme

Reachable without a Bucker session token. The OP redirects back with a code bound to our state.

Parameters

Parameters for GET /sso/oidc/{orgSlug}/callback
NameInTypeNotes
orgSlugrequiredpathstringmin length 1, max length 64
formatquery"redirect" | "json"default "redirect", one of "redirect" | "json"
codequerystringmin length 1, max length 4096
statequerystringmin length 1, max length 512
errorquerystringmax length 256
error_descriptionquerystringmax length 1024

Response 200

{
  user: {
    id: string
    email: string
    name: string
  }
  orgId: string
  role: string
  provisioned: boolean
  relayState: string | null
  tokens: {
    accessToken: string
    refreshToken: string
    expiresIn: number
    tokenType: "Bearer"
  }
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 404 · `not_found` — no such resource, OR one this principal cannot see. The two are deliberately one answer: a 403 would confirm the existence of something whose identifier is guessable.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl https://api.bucker.io/sso/oidc/{orgSlug}/callback

POST/sso/saml/{orgSlug}/acs

No declared credential scheme

Reachable without a Bucker session token. The IdP posts the signed assertion here; the signature is the authentication.

Parameters

Parameters for POST /sso/saml/{orgSlug}/acs
NameInTypeNotes
orgSlugrequiredpathstringmin length 1, max length 64
formatquery"redirect" | "json"default "redirect", one of "redirect" | "json"

Request body (required) · application/json

{
  SAMLResponse: string
  RelayState?: string
}

Response 200

{
  user: {
    id: string
    email: string
    name: string
  }
  orgId: string
  role: string
  provisioned: boolean
  relayState: string | null
  tokens: {
    accessToken: string
    refreshToken: string
    expiresIn: number
    tokenType: "Bearer"
  }
}

Response 400 · `bad_request` — the path, query or body failed validation. `details` carries the Zod issues, one per offending field.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 404 · `not_found` — no such resource, OR one this principal cannot see. The two are deliberately one answer: a 403 would confirm the existence of something whose identifier is guessable.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 429 · `rate_limited` or `quota_exceeded` — over a ceiling. `Retry-After` says when to come back, and `x-ratelimit-limit` / `-remaining` / `-reset` describe the bucket.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Response 500 · `internal_error` — an unhandled failure on this side. The message is always generic; `requestId` is the part worth quoting.

{
  error: {
    code: "bad_request" | "conflict" | "forbidden" | "internal_error" | "mfa_required" | "not_found" | "payload_too_large" | "quota_exceeded" | … 6 more
    message: string
    details?: unknown
    requestId?: string
  }
}

Request

curl -X POST https://api.bucker.io/sso/saml/{orgSlug}/acs \
  -H 'content-type: application/json' \
  -d '{ … }'