Xentree Docs
StatusMy Xentree
StatusMy Xentree
  1. Documentation
  • Documentation
    • Introduction
    • Quickstart
    • Authentication
    • Conversations
    • Knowledge Base
    • Organizations
    • Custom Domains
    • Admin Stats and Members
    • Profile & Analytics
    • Xentree English
    • Xentree Live
    • Skills Connector
    • Integration Tokens
    • Xentree Connect
    • API Keys
    • Webhooks
    • Subscriptions and Entitlements
    • Errors and Limits
  • API Reference
    • Authentication
      • Register a new user
      • Log in and get an access token
      • Refresh an access token
      • Log out the current user
      • Get the current authenticated user
      • Update the current authenticated user
    • Conversations
      • List available AI providers
      • Generate an AI response
      • List conversations for the current user
      • Create a conversation
      • Delete a conversation
      • Get messages for a conversation
      • Start a conversation session
      • Update session activity
      • End a conversation session
      • Get current session status
      • Search indexed user content
      • List notes
      • Create a note
      • Update a note
      • Delete a note
    • Knowledge Base
      • Search the knowledge base
      • List documents
      • Upload document
      • Query knowledge
    • Organizations
      • List organizations for the current user
      • Create an organization
      • Get organization details
      • Update organization details
      • Delete an organization
      • List organization members
      • Add a member to an organization
      • Update a member's role
      • Comprehensive member update (role, organization, team)
      • Remove a member from an organization
    • Custom Domains
      • Configure organization custom domain
      • Get organization custom-domain status
    • Admin Stats & Members
      • Engagement dashboard
      • Engagement member detail
      • Progress KPI cards
      • List learning objectives
      • Create learning objective
      • Archive learning objective
      • Suggest learning objectives (with draft)
      • Progress members table
      • Impact KPI cards
      • List learning targets
      • Create or update learning target
      • Suggest learning targets
      • Export admin stats report as PDF
      • Export admin stats report as XLSX
    • Profile & Analytics
      • Get my XP profile
      • Get a user's public profile
      • Get streak status
      • Get weekly XP activity
      • Get learning pulse (365-day heatmap)
      • Get daily quests
      • Get learning time analytics
    • Xentree English
      • List XEL modules with user progress
      • Get XEL module details
      • Start an XEL module
      • Complete an XEL module
      • Regenerate a task variant
      • Get current XPI breakdown
      • Get XPI history
      • Start a learning session
      • End a learning session
      • Save a transcript entry
      • Get transcripts for a session
      • Submit a learning task response
      • Run AI evaluation for speaking or writing
      • Get the XEL dashboard overview
    • Xentree Live
      • Get remaining Xentree Live quota
      • Start a live session
      • Update an active live session heartbeat
      • End a live session
      • Update the user nickname used in Xentree Live
      • List live transcripts for the current user
      • Save live session transcripts
      • Get a single live transcript
    • Skills Connector
      • List active integrations for the current user
      • Start the Google Calendar OAuth flow
    • Integration Tokens
      • Issue user- or organization-scoped RS256 integration grant token
      • Verify integration token signature and subject claims
      • Revoke integration token by token or jti
      • Rotate integration grant and optionally revoke previous token
      • Get active JWKS material for an integration subject
      • Get active integration key fingerprints for a subject
    • API Keys
      • List API keys
      • Create an API key
      • Delete an API key
    • Health
      • Health check
    • Xentree Connect
      • List Connect Clients
      • Create Connect Client
      • Get Connect Client
      • Update Connect Client
      • List Connected People
      • Toggle Connect Client Active
      • Get Consent Context
      • Deprovision Connect Member
      • Launch Connect
      • Revoke Own Connect Binding
      • Revoke Organization Connect Binding
      • Claim Connect Session
      • List My Connect Bindings
    • Organization Webhooks
      • Create Webhook
      • List Webhooks
      • Get Webhook
      • Update Webhook
      • Delete Webhook
      • List Deliveries
      • Get Delivery
      • Rotate Webhook Secret
      • Send Test Ping
    • Skill Vault
      • List Skills
      • Create Skill
      • Get Active Organization
      • Set Active Organization
      • Clear Active Organization
      • Create Skill Export
      • Download Skill Export
      • Get Skill Summary
      • Get Skill
      • Update Skill
      • Archive Skill
      • Download Skill Md
      • List Skill Grants
      • Grant Skill To Organization
      • Revoke Skill Organization Grant
    • Subscriptions
      • Get My Limits
      • Get My Subscription
      • Get My Usage
      • Cancel Subscription (Stop Renewing)
      • Cancel Queued Renewal (Stop Renewing)
    • Payments
      • Create Xendit Checkout (IDR)
      • Create PayPal Order (USD)
      • Confirm Xendit Payment
      • Capture PayPal Order
      • Get PayPal Client Token
      • Quote Extra Team Seats
      • Buy Extra Team Seats (Xendit)
      • Buy Extra Team Seats (PayPal)
    • Coupons
      • Validate Coupon
    • User Sessions
      • List My Signed-in Devices
      • Sign Out a Device
  • Schemas
    • Error
    • ConfigureCustomDomainRequest
    • SimpleDetailResponse
    • CustomDomainStatus
    • HealthLivenessResponse
    • TokenResponse
    • UserProfile
    • SimpleMessageResponse
    • LogoutResponse
    • ChatRequest
    • ChatResponse
    • ConversationCreateRequest
    • ConversationResponse
    • CoreSearchConversationResult
    • CoreSearchMessageResult
    • CoreSearchResponse
    • NoteCreate
    • NoteUpdate
    • NoteResponse
    • CoreConversationSessionStartResponse
    • CoreConversationSessionActivityResponse
    • CoreConversationSessionEndResponse
    • CoreConversationSessionStatusResponse
    • AdminStatsOrganizationSummary
    • AdminStatsDateRangeSummary
    • AdminStatsKPIStat
    • AdminStatsPulseDay
    • AdminStatsPulseSummary
    • AdminStatsTeamPulse
    • AdminStatsTrendPoint
    • AdminStatsDistributionBucket
    • AdminStatsCohortRetentionCell
    • AdminStatsCohortRetentionRow
    • AdminStatsDashboardResponse
    • AdminStatsCohortAnalyticsRow
    • AdminStatsUserDetailResponse
    • AdminStatsCohortRetentionAnalytics
    • AdminStatsCohortRetentionResponse
    • AdminStatsMemberActivityRow
    • AdminStatsAffiliation
    • AdminStatsMemberDirectoryRow
    • AdminStatsMutationResponse
    • ProgressKPIItem
    • ProgressObjectiveResponse
    • ProgressKPIResponse
    • ProgressKeyResultPayload
    • ProgressObjectiveCreateRequest
    • ProgressObjectiveSuggestRequest
    • ProgressObjectiveKeyResult
    • ProgressObjectiveSuggestedItem
    • ProgressMembersResponse
    • ProgressObjectiveSuggestResponse
    • ProgressTrendResponse
    • ProgressMemberAttempt
    • ProgressMemberSession
    • ProgressMemberRow
    • ProgressDistributionBucket
    • ProgressDistributionResponse
    • ImpactKPIItem
    • ImpactKPIResponse
    • ImpactTrendResponse
    • ImpactLevelItem
    • ImpactLevelProgressionResponse
    • LearningTargetResponse
    • ImpactTeamBreakdownItem
    • ImpactTeamBreakdownResponse
    • ImpactTopPerformerItem
    • ImpactTopPerformersResponse
    • LearningTargetCreateRequest
    • LearningTargetListResponse
    • KnowledgeSearchResponse
    • LearningTargetSuggestResponse
    • RagDocument
    • LVIWeightResponse
    • LVIWeightUpdateRequest
    • IntegrationOut
    • KnowledgeSearchRequest
    • KnowledgeSearchResultItem
    • RagUploadResponse
    • RagQueryRequest
    • RagQueryResponse
    • LiveStartSessionRequest
    • OrganizationResponse
    • LiveSessionResponse
    • LiveQuotaResponse
    • TranscriptTurn
    • XELTaskSubmissionRequest
    • SaveLiveTranscriptsRequest
    • OrganizationCreateRequest
    • MemberResponse
    • XELModuleSummary
    • XELModuleDetail
    • XELTaskSubmissionResponse
    • XELEvaluationResponse
    • XELDashboardResponse
    • XELModuleStartResponse
    • XELSessionStartRequest
    • XELModuleCompleteResponse
    • XELTaskRegenerateRequest
    • XELTaskRegenerateResponse
    • XELXpiResponse
    • XELXpiHistoryItem
    • XELSessionStartResponse
    • XELSessionEndRequest
    • XELSessionEndResponse
    • XELTranscriptSaveRequest
    • XELTranscriptSaveResponse
    • XELTranscriptItem
    • LiveSessionEndResponse
    • LiveSessionHeartbeatResponse
    • LiveNicknameResponse
    • LiveTranscriptSaveResponse
    • LiveTranscriptListItem
    • LiveTranscriptListResponse
    • LiveTranscriptDetailResponse
    • XPProfileResponse
    • PublicXPProfileResponse
    • StreakStatusResponse
    • WeeklyActivityResponse
    • LearningPulseDayEntry
    • APIKeyResponse
    • LearningPulseResponse
    • IntegrationGrantRequest
    • QuestItem
    • IntegrationGrantResponse
    • QuestsResponse
    • IntegrationVerifyRequest
    • LearningTimeAnalyticsResponse
    • IntegrationVerifyResponse
    • APIKeyCreateRequest
    • IntegrationRevokeRequest
    • IntegrationRevokeResponse
    • IntegrationJWKSResponse
    • IntegrationFingerprintResponse
    • ConnectClientResponse
    • ConnectClientCreateRequest
    • IntegrationRotateRequest
    • ConnectClientUpdateRequest
    • IntegrationRotateResponse
    • ConnectedPersonResponse
    • WebhookCreate
    • WebhookCreated
    • WebhookOut
    • WebhookUpdate
    • DeliveryPage
    • WebhookDeliveryOut
    • WebhookDeliveryDetail
    • TestPingAccepted
    • SkillMarkdownInput
    • ActiveOrganizationInput
    • GrantInput
    • ConsentContextRequest
    • Body_deprovision_connect_member_connect_v1_deprovision__client_id__post
    • Body_launch_connect_connect_v1_launch__client_id__post
    • SessionClaimRequest
    • ErrorResponse
    • RenewalWindowClosedError
    • NewOrganizationSpec
    • CheckoutRequest
    • XenditCheckoutResponse
    • PayPalOrderResponse
    • PaymentConfirmResponse
    • SeatRequest
    • SeatQuote
    • CouponValidationResponse
    • FlexibleEnterpriseDetail
    • SubscriptionMeResponse
    • CancelSubscriptionResponse
    • CancelQueuedResponse
    • UserSession
    • UserSessionList
    • ConsentContextResponse
    • DeprovisionResponse
    • MyConnectBinding
  1. Documentation

Xentree Connect

Xentree Connect lets a partner platform (an intranet, an HR system, or a learning management system) send its users into Xentree with one click, without a second password. The partner signs a short-lived token with its own private key, the user's browser posts it to Xentree, and Xentree verifies it against the public key the organization administrator registered.

How it works#

Connect is a signed-JWT launch with account binding. It is not OIDC, LTI or SAML: there is no authorization-code exchange and no discovery document.
1.
Register. An organization administrator registers an integration in Xentree: client ID, issuer, audience and the partner's public keys (JWKS).
2.
Launch. A signed-in user of the partner platform clicks a link. The partner signs an assertion (a JWT) and the browser posts it to POST /connect/v1/launch/{client_id}.
3.
Verify. Xentree checks the signature, issuer, audience, expiry and organization, rejects a reused jti, and stores a one-time ticket that is valid for 90 seconds (300 seconds when consent is needed).
4.
Consent (first launch only). Xentree answers 303 See Other to /connect/consent?ticket=…. The user signs in to their existing Xentree account and agrees. Xentree then binds the partner's sub to that Xentree user.
5.
Session. Xentree claims the ticket and sets HttpOnly session cookies. The user lands on /learn.
6.
Later launches. While the binding is active and the consent text is current, the consent page shows nothing: the ticket is claimed immediately and the user passes straight through in one click.
Important behaviors:
Xentree does not create user accounts from a launch. The user must already have a Xentree account. The email and name claims, if present, are ignored. Identity comes from the Xentree sign-in during consent.
Membership. If the user is not yet a member of the organization, they are added only when the integration has auto_enroll turned on ("Use a seat automatically on first Connect"). They join as a plain member and, if the integration has a default_team_id, join that team. With auto_enroll off, a non-member is refused with 403.
Seats. With auto_enroll, one seat is taken. On a plan with a seat limit, a full organization is refused with 403 and error: "seat_limit_reached". On a plan with unlimited seats the check always passes.
Revocation. A binding can be revoked by the user, by an organization administrator, or by the partner through the deprovision endpoint. Revoking ends every session issued from that binding.
Entitlement. Connect must be enabled for the organization (connect_entitlement). Otherwise launch and admin calls return 403.

Base URLs#

Launch, consent and deprovision endpoints are served on the host where Xentree is served for the organization: the main application host, or the organization's verified custom domain. A ticket is bound to the host that issued it, and a custom-domain host only accepts integrations of its own organization.

Endpoint reference#

POST /connect/v1/launch/{client_id}#

Starts a launch. Called by the user's browser, as a form post. It is not a bearer-token endpoint.
ItemValue
AuthenticationThe signed assertion in the form body. No Authorization header.
Content typeapplication/x-www-form-urlencoded
Rate limit120 requests per minute per IP address
Path parameter
NameDescription
client_idThe Client ID of the registered integration
Form parameter
NameTypeRequiredDescription
assertionstringYesThe signed JWT described below
Assertion header
FieldRule
algRS256 or ES256. Other algorithms are rejected.
kidRequired. Must match a key in the registered JWKS.
Assertion claims
ClaimRequiredRule
issYesExactly equal to the integration's Issuer
audYesExactly equal to the integration's Audience
subYesStable, unique identifier of the user in the partner system. Do not use a value that changes, such as an email address.
organization_idYesThe Xentree organization ID that owns the integration. A different value returns 403.
iatYesIssue time (seconds since epoch)
expYesExpiry (seconds since epoch). Use 60 seconds or less. No clock-skew leeway is applied.
jtiYesUnique per token. A jti can be used once.
Lifecycle
Partner browser                                   Xentree
      | POST /connect/v1/launch/{client_id}          |
      |   assertion=<JWT> -------------------------> |  verify, store one-time ticket
      | <---- 303  Location: /connect/consent?ticket=T
      | POST /connect/v1/consent-context {ticket_id} |  who is asking, is consent needed
      | POST /connect/v1/session/claim               |
      |   {ticket_id, consent} ---------------------> |  bind (first time), consume ticket
      | <---- 200 {"redirect":"/learn"}  + Set-Cookie (HttpOnly access and refresh cookies)
Responses
StatusMeaning
303Success. Location is /connect/consent?ticket=…. The ticket is opaque and never contains user data.
401Invalid Connect launch assertion: bad signature, unknown kid, wrong iss or aud, missing required claim, or expired
403Connect is not enabled for this organization; Connect assertion organization mismatch; or the integration is not permitted on this custom domain
404Connect client not found: unknown client_id, or the integration is inactive
409Connect launch assertion was already used: the jti was seen before
429Rate limit exceeded
503Replay protection is temporarily unavailable. Retry.

POST /connect/v1/consent-context#

Used by the consent page. Peeks at a ticket without consuming it.
Request body: {"ticket_id": "<ticket>"}. Response: organization_name, policy_url, consent_required, auto_enroll. Errors: 401 (ticket invalid or expired), 403 (ticket belongs to another host).

POST /connect/v1/session/claim#

Used by the consent page. Claims the one-time ticket and issues the session cookies.
Request body: {"ticket_id": "<ticket>", "consent": true}.
StatusMeaning
200{"redirect": "/learn"} and HttpOnly session cookies
401Ticket invalid, expired or already claimed; or the user is not signed in to Xentree when consent is needed
403The user is not an organization member and auto_enroll is off; the ticket belongs to another host; or seat_limit_reached
409consent was not granted when it is required, or the identity binding is already active

POST /connect/v1/deprovision/{client_id}#

Offboarding from the partner platform. Called server to server by the partner.
Content type application/x-www-form-urlencoded, form field assertion.
The assertion follows the same rules as launch and must also carry "purpose": "deprovision". A launch assertion cannot be replayed to remove a user.
Idempotent. It revokes the user's binding, ends every session issued from it, and emits a connect.consent.revoked webhook event.
If the user's role is plain member and no other active binding connects them to the organization, they are also removed from the organization and its teams, and the seat is released. Administrators and managers are never removed by an external system.
StatusMeaning
200`{"status": "...", "seat_released": true
401Invalid assertion
403Assertion purpose is not deprovision, or organization mismatch
404Unknown client_id or inactive integration
409jti already used

User and administrator binding endpoints#

MethodPathAuthenticationPurpose
GET/connect/v1/me/bindingsBearer tokenThe signed-in user's active connections
DELETE/connect/v1/me/bindings/{binding_id}Bearer tokenThe user withdraws their own connection (204)
DELETE/connect/v1/organizations/{organization_id}/bindings/{binding_id}Bearer token, organization administratorRevoke a user's connection (204). Add ?remove_member=true to also release the seat of a plain member.

Administrator endpoints#

Base path: /api/admin/organizations/{org_id}/connect-clients. All calls need a bearer token of an organization administrator, and the organization needs the Connect entitlement (403 otherwise).
MethodPathPurpose
GET/List integrations
POST/Register an integration (201)
GET/{client_record_id}Read an integration, including its public JWKS
PATCH/{client_record_id}Update fields. Sending jwks_json rotates the keys.
POST/{client_record_id}/toggle-activeActivate or deactivate. A deactivated integration refuses new launches.
GET/{client_record_id}/bindingsUsers currently connected through this integration (up to 500)
client_record_id is the id returned by the API, not the client_id string.
Create request body
FieldTypeRequiredDescription
namestring (1 to 255)YesDisplay name
client_idstring (1 to 128)YesPublic identifier, unique across Xentree. Used in the launch URL.
issuerstring (1 to 255)YesMust equal the assertion iss
audiencestring (1 to 255)YesMust equal the assertion aud
jwks_jsonstringYesThe partner's public key set, as JSON text. See below.
descriptionstringNoFree text
policy_urlstring (max 512)NoLink to the partner's data policy, shown on the consent screen. Only absolute http or https URLs are shown.
auto_enrollbooleanNo (default false)"Use a seat automatically on first Connect". When true, a user who consents and is not yet a member joins the organization and takes a seat.
default_team_idstringNoA team of the same organization. Users added by auto_enroll join it.
Errors: 400 (jwks_json is not valid JSON, has no keys array or no keys; or default_team_id is not a team of this organization), 403 (not an administrator, or Connect not enabled), 409 (client_id already exists), 503 (CONNECT_ADMIN_DATABASE_UNAVAILABLE).

Partner webhooks#

Each integration can subscribe up to 3 HTTPS endpoints to signed events (erpi.session.completed, connect.consent.revoked). Base path: /api/admin/organizations/{org_id}/connect-clients/{client_record_id}/webhooks.
MethodPathPurpose
GET/eventsEvent catalog
GET, POST/List, create. The signing secret is shown once, in the create response.
GET, PATCH, DELETE/{webhook_id}Read, update, delete
POST/{webhook_id}/reactivateRe-enable after failures
POST/{webhook_id}/rotate-secretNew secret. The previous one stays valid for 24 hours.
POST/{webhook_id}/testSend a test ping (202)
GET/{webhook_id}/deliveriesDelivery log
GET/{webhook_id}/deliveries/{delivery_id}One delivery
POST/{webhook_id}/deliveries/{delivery_id}/replaySend a delivery again
Receiving endpoints must use HTTPS on port 443 with a public domain name. Redirects count as failures.

Integration guide for partner developers#

1. Create a key pair#

Generate an RSA 2048 (or EC P-256) key pair. Keep the private key on your server. Never send it to Xentree.

2. Publish the public key as a JWKS#

Give the Xentree organization administrator a JSON Web Key Set that holds public keys only. Every key must have a kid; the token header kid must match it.
RSA:
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "partner-2026-10",
      "use": "sig",
      "alg": "RS256",
      "n": "<base64url modulus>",
      "e": "AQAB"
    }
  ]
}
EC P-256:
{
  "keys": [
    {
      "kty": "EC",
      "kid": "partner-ec-2026-10",
      "use": "sig",
      "alg": "ES256",
      "crv": "P-256",
      "x": "<base64url x>",
      "y": "<base64url y>"
    }
  ]
}
Rules: the object must contain a non-empty keys array, and Xentree reads only the pasted JSON. It does not fetch a jwks_uri. To rotate, add the new key (keep the old one until all in-flight launches finish) and send the new JSON with a PATCH or Rotate JWKS in the administrator UI.

3. Agree on the values#

ValueWhere it goes
client_idIn the launch URL, and registered in Xentree
issuerAssertion iss, and registered in Xentree
audienceAssertion aud, and registered in Xentree
Organization IDAssertion organization_id (ask your Xentree administrator)

4. Python (Flask)#

5. Node.js (Express)#

6. Deprovision a user#

Sign a second assertion with the same claims plus "purpose": "deprovision" and post it from your server:

Checklist#

Sign with RS256 or ES256, and include the kid header.
iss, aud and organization_id match the registration exactly.
sub is stable and unique per user.
exp is at most a minute away, and every token has a fresh jti.
The launch is a form POST from the user's browser, never a GET link and never a call from your server.
Your server clock is synchronized. No skew is tolerated.
Every user who launches already has a Xentree account.
Modified at 2026-10-24 07:50:45
Previous
Integration Tokens
Next
API Keys
Built with