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
    • 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
      • Search infrastructure status
      • Rebuild search index
      • 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
      • Admin observability for integration token revocations
    • API Keys
      • List API keys
      • Create an API key
      • Delete an API key
    • Health
      • Health check
      • Detailed 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
    • 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
  • Schemas
    • Error
    • ConfigureCustomDomainRequest
    • SimpleDetailResponse
    • CustomDomainStatus
    • HealthLivenessResponse
    • HealthDetailedResponse
    • HealthDetailedServices
    • DatabaseHealthStatus
    • DatabasePoolStatus
    • TokenResponse
    • DatabaseConnectionStats
    • UserProfile
    • SimpleMessageResponse
    • LogoutResponse
    • ChatRequest
    • ChatResponse
    • ConversationCreateRequest
    • ConversationResponse
    • CoreSearchConversationResult
    • CoreSearchMessageResult
    • CoreSearchResponse
    • TypesenseReindexRequest
    • TypesenseStatusResponse
    • NoteCreate
    • NoteUpdate
    • NoteResponse
    • CoreConversationSessionStartResponse
    • CoreConversationSessionActivityResponse
    • CoreConversationSessionEndResponse
    • CoreConversationSessionStatusResponse
    • AdminStatsProgressMemberRow
    • TenantResponse
    • AdminStatsProgressMembersResponse
    • 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
    • IntegrationTokenRevocationItem
    • 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
  1. Documentation

Webhooks

Organization webhooks push events to your HTTPS endpoint as they happen, so you can integrate Xentree in real time instead of polling.

Overview#

Webhooks belong to an organization and are managed by organization administrators.
Creating a webhook requires an active Team (or Enterprise) plan. Without it, create, update (other than disabling), rotate-secret, and test-ping return 403 with error: "feature_not_in_plan". Listing, reading, disabling, deleting, and viewing deliveries stay available after a downgrade.
Limits: 20 webhooks per organization and 256 KB per event payload. A 21st webhook returns 409.
Each webhook subscribes to one or more of the 30 event types. One URL can be registered once per organization (duplicates return 409).
Endpoints must be public HTTPS URLs. Private, loopback, link-local, and otherwise internal addresses are rejected (400 on create/update, and blocked again at delivery time). Redirects are never followed.
Respond with any 2xx within 10 seconds (5 seconds to connect). Response bodies are stored truncated to 2 KB for the delivery log.

Managing webhooks#

All routes live under /api/organizations/{organization_id}/webhooks and accept a bearer token or an API key belonging to an organization administrator.
MethodPathResult
POST/webhooks201. Returns the webhook and its signing secret, shown only once
GET/webhooks200. List webhooks
GET/webhooks/{webhook_id}200. Get one webhook
PATCH/webhooks/{webhook_id}200. Update url, events, or status (active / disabled)
DELETE/webhooks/{webhook_id}204
POST/webhooks/{webhook_id}/rotate-secret200. New secret, old one stays valid for 24 hours
POST/webhooks/{webhook_id}/test202. Queues a webhook.test.ping delivery (409 if the webhook is disabled)
GET/webhooks/{webhook_id}/deliveries200. Query: limit (1–100, default 25), cursor, status
GET/webhooks/{webhook_id}/deliveries/{delivery_id}200. Includes the payload as sent (null once redacted)
Example create request:
{
  "url": "https://example.com/hooks/xentree",
  "events": ["xel.module.completed", "organization.member.added"]
}
Example response (secret only appears here):
{
  "id": "…",
  "organization_id": "…",
  "url": "https://example.com/hooks/xentree",
  "events": ["organization.member.added", "xel.module.completed"],
  "status": "active",
  "disabled_reason": null,
  "consecutive_failures": 0,
  "secret_masked": "whsec_...a1b2",
  "previous_secret_valid_until": null,
  "created_at": "2026-10-13T10:00:00Z",
  "updated_at": "2026-10-13T10:00:00Z",
  "secret": "whsec_<64 hex characters>"
}
Unknown event names return 400 with the list of supported events. Store the secret securely; it cannot be retrieved later, only rotated.

Event envelope#

Every delivery is a POST with a JSON body:
{
  "id": "evt_3f9c0d2e7a1b4c5d8e6f1a2b3c4d5e6f",
  "event": "xel.module.completed",
  "organization_id": "org_…",
  "created_at": "2026-10-13T10:15:30.123456Z",
  "data": { }
}
Request headers:
HeaderMeaning
X-Xentree-EventEvent name
X-Xentree-DeliveryDelivery ID (one per delivery, same across retries)
X-Xentree-Idempotency-KeyStable key; use it to de-duplicate retries
X-Xentree-Key-IdID of the secret used for signing
X-Xentree-SignatureSignature, see below
Credential-like fields are stripped from data before sending. Treat deliveries as at-least-once: de-duplicate on id or the idempotency key.

Verifying signatures#

Always verify the signature against the raw request body bytes, before parsing JSON.
X-Xentree-Signature: t=<timestamp>,v1=<signature>
t is a Unix timestamp in seconds.
v1 is the lowercase hex HMAC-SHA256, keyed with your whsec_… secret, of the string "<timestamp>.<raw_body>".
Reject the request if t is more than 300 seconds from your clock (replay protection).
Compare signatures in constant time.

Zero-downtime secret rotation#

After rotate-secret, the old secret stays valid for 24 hours. During that window each delivery carries two v1 values, the new secret first and the old one second:
X-Xentree-Signature: t=1760350530,v1=<signed with new secret>,v1=<signed with old secret>
Accept the request if any v1 matches your secret. Deploy the new secret to your receiver at any point in the window; nothing is dropped. The previous_secret_valid_until field on the webhook shows when the old secret expires.

Connect partners#

Webhooks delivered to Xentree Connect partners keep their original header format for backwards compatibility:
X-Xentree-Timestamp: <timestamp>
X-Xentree-Signature: v1=<hmac>, computed over "<timestamp>.<delivery_id>.<raw_body>" where delivery_id is X-Xentree-Delivery
X-Xentree-Signature-V2: the standard t=<timestamp>,v1=<signature> format described above. New partner integrations should verify this header.

Python#

Flask usage: verify_xentree_signature(request.get_data(), request.headers["X-Xentree-Signature"], SECRET).

Node.js / TypeScript#

import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SECONDS = 300;

export function verifyXentreeSignature(
  rawBody: Buffer,
  header: string,
  secret: string,
): boolean {
  let timestamp: number | null = null;
  const candidates: string[] = [];

  for (const part of header.split(",")) {
    const idx = part.indexOf("=");
    if (idx < 0) return false;
    const key = part.slice(0, idx).trim();
    const value = part.slice(idx + 1).trim();
    if (key === "t" && timestamp === null) {
      timestamp = Number.parseInt(value, 10);
      if (Number.isNaN(timestamp)) return false;
    } else if (key === "v1") {
      candidates.push(value);
    }
  }
  if (timestamp === null || candidates.length === 0) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest("hex");
  const expectedBuf = Buffer.from(expected);

  let matched = false;
  for (const candidate of candidates) {
    const buf = Buffer.from(candidate);
    if (buf.length === expectedBuf.length && timingSafeEqual(buf, expectedBuf)) {
      matched = true; // keep looping so timing does not reveal which one matched
    }
  }
  return matched;
}
Express usage: mount express.raw({ type: "application/json" }) on the webhook route so req.body is the unmodified Buffer.

Event catalog#

30 events in 9 categories. Active events are emitted today. Roadmap events are registered and can be subscribed to, but nothing emits them yet. Subscribing now means they start arriving without any change on your side once released.
Active: 16 · Roadmap: 14

Xentree English (XEL) & Learning#

EventStatusDescription
xel.module.startedActiveA member started a XEL module
xel.module.completedActiveA member completed a XEL module
xel.task.submittedActiveA learning task was submitted
xel.evaluation.completedActiveAI finished evaluating a speaking or writing task
xel.session.endedActiveA XEL learning session ended
xel.xpi.updatedRoadmapA member's proficiency index (XPI) changed

Live Sessions & Transcripts#

EventStatusDescription
live.session.startedActiveA live voice or avatar session started
live.session.endedActiveA live session finished
live.transcript.savedActiveA live session transcript was saved
live.quota.warningRoadmapRemaining live quota reached the warning threshold

Gamification & Progress#

EventStatusDescription
gamification.level_upActiveA member reached a new level
gamification.streak.milestoneActiveA member hit a 7 or 30 day streak multiple
gamification.xp_earnedRoadmapA member earned XP
gamification.quest.completedRoadmapA daily quest was completed

Conversations & AI Notes#

EventStatusDescription
conversation.createdRoadmapA conversation was created
conversation.session.endedRoadmapAn AI chat session ended
conversation.note.createdRoadmapA note was created from a conversation

Knowledge Base (RAG)#

EventStatusDescription
knowledge.document.uploadedRoadmapA document was uploaded to the knowledge base
knowledge.document.indexedRoadmapA document finished indexing and is queryable

Organization & Members#

EventStatusDescription
organization.member.addedActiveA member was added to the organization
organization.member.removedActiveA member was removed from the organization
organization.member.role_updatedActiveA member's role changed
organization.custom_domain.status_changedRoadmapCustom domain DNS/SSL status changed

Admin Stats & Objectives#

EventStatusDescription
stats.objective.createdRoadmapA learning objective (OKR) was created
stats.objective.archivedRoadmapA learning objective was archived
stats.target.updatedRoadmapA KPI/impact target was updated

Security & Integrations#

EventStatusDescription
api_key.createdActiveAn API key was created
api_key.deletedActiveAn API key was revoked
integration.token.revokedRoadmapAn integration grant token was revoked

System#

EventStatusDescription
webhook.test.pingActiveSent by the /test endpoint

Delivery, retries, and circuit breaker#

Your endpoint's response decides what happens next:
ResponseOutcome
2xx or 409Delivered. Failure counter resets
408, 429, 5xx, network error, timeoutRetried
410 GoneDelivery marked dead and the webhook is disabled immediately
Other 4xx, 3xxPermanent failure, not retried
Retry schedule. Retries use full jitter: each wait is random between zero and a ceiling that grows per attempt: 1 min, 5 min, 30 min, 2 h, 6 h, then stays at 6 hours. A delivery gets up to 12 attempts before it is marked dead.
Circuit breaker. Failures are counted per webhook, and any success resets the counter.
Consecutive failuresWebhook status
10degraded. Still delivering; returns to active after a success
50disabled. Delivery stops and disabled_reason explains why
A 410 response disables the webhook at once, regardless of the counter. To resume, fix your endpoint and PATCH the webhook with "status": "active", then send a test ping.
Delivery statuses you may see in the log: pending, leased, retrying, delivered, dead, cancelled.

Best practices#

Return 2xx quickly and process asynchronously.
Verify the signature on the raw body and enforce the 300-second window.
De-duplicate on the event id or X-Xentree-Idempotency-Key.
Subscribe only to the events you need.
Rotate secrets periodically and use the 24-hour overlap for zero-downtime changes.
Modified at 2026-10-13 16:09:40
Previous
API Keys
Next
Subscriptions and Entitlements
Built with