openLesson
UpgradeDashboard

OpenLesson Agentic API v2

Performance Workspace API Reference

Full request and response specifications for every Agentic API endpoint: workspaces, evidence, performance analysis, GHL links, guest provisioning, and dashboard key management. Bearer endpoints use base path /api/v2/agent and require active pro_teams.

Agent skill file →Get API key

Authentication

Authorization: Bearer <api_key>
Content-Type: application/json

Key types

FieldTypeRequiredDescription
sk_string prefix—Organization member key from dashboard or POST /api/v2/agent/keys (browser session).
gsk_string prefix—Guest key from POST /api/v2/agent/org/guests.

Error response

{
  "error": {
    "code": "forbidden",
    "message": "Human-readable explanation",
    "details": {}
  }
}

Common codes: unauthorized, key_revoked, key_expired, forbidden, teams_required, validation_error, workspace_not_found, block_not_found, ghl_link_not_found, guest_not_found, not_found, rate_limit_exceeded (429).

Scopes

Each Bearer-authenticated endpoint requires one scope. The wildcard * grants all scopes.

Scope reference

FieldTypeRequiredDescription
workspaces:readscope—List blocks; run performance analysis (report or chat).
workspaces:writescope—Create workspaces; upload evidence.
ghl:readscope—List GHL links; poll results.
ghl:writescope—Create GHL Score links for blocks.
org:readscope—Reserved for org admin keys (future org read endpoints).
org:writescope—Create guest users and issue gsk_ keys.
*scope—All scopes. Org admins only when assigning to sk_ keys.

Default sk_ key scopes: workspaces:read, workspaces:write, ghl:read, ghl:write. Guest gsk_ keys receive the same four scopes automatically.

Rate limits

API keys default to 120 requests per minute per key. Exceeding the limit returns 429 with code rate_limit_exceeded.

Endpoint index

  • POST /api/v2/agent/workspaces
  • GET /api/v2/agent/workspaces/{workspace_id}/blocks
  • POST /api/v2/agent/workspaces/{workspace_id}/evidence
  • POST /api/v2/agent/workspaces/{workspace_id}/performance
  • POST /api/v2/agent/workspaces/{workspace_id}/blocks/{block_id}/ghl-links
  • GET /api/v2/agent/workspaces/{workspace_id}/ghl-links
  • GET /api/v2/agent/workspaces/{workspace_id}/ghl-links/{link_id}/results
  • POST /api/v2/agent/org/guests
  • GET /api/v2/agent/keys
  • POST /api/v2/agent/keys
  • DELETE /api/v2/agent/keys/{key_id}
  • PATCH /api/v2/agent/keys/{key_id}/scopes
POST/api/v2/agent/workspacesworkspaces:write201 Created

Create a Performance Workspace from an initial prompt and optional seed files.

Request body (JSON)

FieldTypeRequiredDescription
initial_promptstringyesTask or learning goal used to generate workspace title and blocks.
filesarray<object>—Optional seed files (max 5). Each item: name (string), mime_type (string), data (base64 string).
files[].namestringyesFilename including extension.
files[].mime_typestringyesapplication/pdf, text/plain, text/markdown, image/jpeg, image/png, image/webp.
files[].datastring (base64)yesFile bytes, max 10 MB per file.

Request example

{
  "initial_prompt": "Prepare a CSM to handle enterprise renewal negotiations.",
  "files": [
    {
      "name": "brief.md",
      "mime_type": "text/markdown",
      "data": "<base64>"
    }
  ]
}

Response body

FieldTypeRequiredDescription
workspace.iduuid—Workspace ID (learning_plans.id).
workspace.titlestring—Generated workspace title.
workspace.root_topicstring—Truncated prompt summary.
workspace.statusstring—active
workspace.notesstring—Full initial prompt stored as notes.
workspace.created_atISO-8601—Creation timestamp.
workspace.updated_atISO-8601—Last update timestamp.
blocksarray—Generated assessable blocks (3–8).
blocks[].iduuid—Block ID (plan_nodes.id).
blocks[].titlestring—Block title.
blocks[].descriptionstring—What the learner should demonstrate.
blocks[].is_startboolean—True for the entry block.
blocks[].next_node_idsuuid[]—Linked next blocks in the graph.
blocks[].statusstring—available
blocks[].created_atISO-8601—Block creation timestamp.
filesarray—Uploaded plan_files records (empty if none).
files[].iduuid—plan_files.id
files[].file_namestring—Original filename.
files[].file_sizeinteger—Bytes.
files[].mime_typestring—MIME type.
files[].created_atISO-8601—Upload timestamp.

Response example

{
  "workspace": {
    "id": "a3090aa0-3498-4be8-aa87-32a1a6591641",
    "title": "Enterprise Renewal Negotiation Prep",
    "root_topic": "Prepare a CSM to handle enterprise renewal negotiations.",
    "status": "active",
    "notes": "Prepare a CSM to handle enterprise renewal negotiations.",
    "created_at": "2026-06-23T13:01:32.64659+00:00",
    "updated_at": "2026-06-23T13:01:32.64659+00:00"
  },
  "blocks": [
    {
      "id": "e57844a6-1b69-465c-9120-d0812d6339ae",
      "title": "Context & Procurement Tactics",
      "description": "Demonstrate knowledge of renewal cycles and procurement pushback.",
      "is_start": true,
      "next_node_ids": ["b454f31a-3045-4c23-a60e-820b43d0e9ce"],
      "status": "available",
      "created_at": "2026-06-23T13:01:32.691293+00:00"
    }
  ],
  "files": []
}
  • Guest keys (gsk_) may create workspaces; workspace is org-owned and tagged with guest_user_id.
  • Requires Teams tier (403 teams_required otherwise).
GET/api/v2/agent/workspaces/{workspace_id}/blocksworkspaces:read200 OK

List assessable blocks in a workspace.

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.

Response body

FieldTypeRequiredDescription
blocksarray—All plan_nodes for the workspace, ordered by created_at ascending.
blocks[].iduuid—Block ID.
blocks[].titlestring—Block title.
blocks[].descriptionstring—Demonstration objective.
blocks[].is_startboolean—Entry block flag.
blocks[].next_node_idsuuid[]—Next block IDs.
blocks[].statusstring—available | in_progress | completed
blocks[].created_atISO-8601—Creation timestamp.

Response example

{
  "blocks": [
    {
      "id": "e57844a6-1b69-465c-9120-d0812d6339ae",
      "title": "Context & Procurement Tactics",
      "description": "Demonstrate knowledge of renewal cycles.",
      "is_start": true,
      "next_node_ids": ["b454f31a-3045-4c23-a60e-820b43d0e9ce"],
      "status": "available",
      "created_at": "2026-06-23T13:01:32.691293+00:00"
    }
  ]
}
  • 404 workspace_not_found if the key cannot access the workspace.
POST/api/v2/agent/workspaces/{workspace_id}/evidenceworkspaces:write201 Created

Upload tool usage, screenshots, video, or EEG to xAI Files and link to workspace/block/session.

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.

Request body (JSON)

FieldTypeRequiredDescription
typestringyestool | screen | screenshot | video | eeg (screenshot aliases to screen).
mime_typestringyesMust match type (see MIME table below).
datastring (base64)yesArtifact bytes, max 10 MB.
file_namestring—Optional filename; default derived from type.
block_iduuid—Optional block to scope evidence.
session_iduuid—Optional linked session ID.
timestamp_msinteger—Client timestamp; defaults to server time.
chunk_indexinteger—Chunk sequence for streaming artifacts; default 0.
metadataobject—Arbitrary JSON metadata stored on the evidence row.
tool_namestring—For type=tool: tool identifier (e.g. canvas, pumadoc).
tool_actionstring—For type=tool: action name (e.g. draw, step_completed).
band_powersobject—For type=eeg: band power map (numeric values).
device_namestring—For type=eeg: device label (e.g. Muse).
sample_countinteger—For type=eeg: sample count in chunk.

Request example

{
  "type": "tool",
  "file_name": "renewal-workbench-trace.json",
  "mime_type": "application/json",
  "data": "<base64>",
  "block_id": "e57844a6-1b69-465c-9120-d0812d6339ae",
  "metadata": { "source": "pumadoc-customer-agent" },
  "tool_name": "renewal-workbench",
  "tool_action": "session_trace"
}

Response body

FieldTypeRequiredDescription
evidence.iduuid—workspace_evidence.id
evidence.workspace_iduuid—Same as plan_id.
evidence.block_iduuid | null—plan_node_id if scoped.
evidence.session_iduuid | null—Optional session link.
evidence.typestring—tool | screen | video | eeg
evidence.file_namestring—Stored filename.
evidence.mime_typestring—MIME type.
evidence.file_sizeinteger—Decoded byte length.
evidence.xai_file_idstring—xAI Files API file_id.
evidence.timestamp_msinteger—Client or server timestamp.
evidence.chunk_indexinteger—Chunk index.
evidence.metadataobject—Stored metadata JSON.
evidence.tool_namestring | null—Tool name when type=tool.
evidence.tool_actionstring | null—Tool action when type=tool.
evidence.device_namestring | null—EEG device when type=eeg.
evidence.sample_countinteger | null—EEG samples when type=eeg.
evidence.created_atISO-8601—Upload timestamp.

Response example

{
  "evidence": {
    "id": "3d2a15c4-3e21-4e11-bf9c-c007ef0c82b4",
    "workspace_id": "a3090aa0-3498-4be8-aa87-32a1a6591641",
    "block_id": "e57844a6-1b69-465c-9120-d0812d6339ae",
    "session_id": null,
    "type": "tool",
    "file_name": "renewal-workbench-trace.json",
    "mime_type": "application/json",
    "file_size": 992,
    "xai_file_id": "file_9f395df1-ecfd-4587-87fd-4f2e8d3cea3d",
    "timestamp_ms": 1782219694763,
    "chunk_index": 0,
    "metadata": { "source": "pumadoc-customer-agent" },
    "tool_name": "renewal-workbench",
    "tool_action": "session_trace",
    "device_name": null,
    "sample_count": null,
    "created_at": "2026-06-23T13:01:34.791176+00:00"
  }
}
  • MIME by type: tool → application/json, text/plain, text/markdown; screen → image/png, image/jpeg, image/webp; video → video/mp4, video/webm, video/quicktime; eeg → application/json, text/plain.
  • 404 block_not_found if block_id is not in this workspace.
POST/api/v2/agent/workspaces/{workspace_id}/performanceworkspaces:read200 OK

Analyze workspace evidence, GHL results, sessions, and plan files. Report mode (no prompt) or chat mode (with prompt).

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.

Request body (JSON)

FieldTypeRequiredDescription
promptstring—If non-empty → chat mode (markdown response). If omitted or empty → report mode (structured JSON).
block_iduuid—Optional: scope analysis to one block.
conversation_historyarray—Chat mode only. Up to 12 prior turns: { role: user|assistant, content: string }.
file_idsstring[]—Optional xAI file IDs from a prior performance call. Empty → rebuild context bundle.

Request example

// Report mode
{ "block_id": "e57844a6-1b69-465c-9120-d0812d6339ae" }

// Chat mode
{
  "block_id": "e57844a6-1b69-465c-9120-d0812d6339ae",
  "prompt": "What is the single biggest readiness gap?",
  "file_ids": ["file_814439bd-4894-4e11-852d-314e9f777a7f"]
}

Response body

FieldTypeRequiredDescription
modereport | chat—Which response shape is populated.
reportobject | null—Present when mode=report.
report.summarystring—Executive summary.
report.strengthsstring[]—Demonstrated strengths.
report.growth_areasstring[]—Areas needing development.
report.gap_analysis.summarystring—Gap analysis overview.
report.gap_analysis.gapsarray—title, evidence, severity (low|medium|high), suggested_repair.
report.gap_analysis.next_practicestring[]—Recommended practice actions.
report.suggestionsstring[]—Additional recommendations.
report.confidencestring—emerging | developing | clear | well-connected
responsestring | null—Markdown answer when mode=chat.
evidence_summaryobject | null—Counts used in context (blocks, ghl_sessions, evidence_artifacts, linked_sessions, plan_files).
file_idsstring[]—xAI file IDs for follow-up calls; pass back as file_ids.

Response example

{
  "mode": "report",
  "report": {
    "summary": "Learner prepared for renewal negotiation using simulated tool traces.",
    "strengths": ["Used CRM and ROI table before price discussion"],
    "growth_areas": ["Did not quantify churn risk"],
    "gap_analysis": {
      "summary": "Missing churn risk quantification.",
      "gaps": [
        {
          "title": "Missing churn risk quantification",
          "evidence": "Reflection states churn risk was not modeled.",
          "severity": "medium",
          "suggested_repair": "Add probability-weighted revenue loss to ROI table."
        }
      ],
      "next_practice": ["Run 3 simulated procurement scenarios with churn math"]
    },
    "suggestions": ["Practice live role-play with procurement pushback"],
    "confidence": "emerging"
  },
  "evidence_summary": {
    "blocks": 1,
    "ghl_sessions": 0,
    "evidence_artifacts": 2,
    "linked_sessions": 0,
    "plan_files": 0
  },
  "file_ids": ["file_814439bd-4894-4e11-852d-314e9f777a7f"]
}

// Chat mode response
{
  "mode": "chat",
  "response": "The biggest readiness gap is **churn risk quantification** — the learner discussed renewal value but never modeled probability-weighted revenue loss.",
  "evidence_summary": {
    "blocks": 1,
    "ghl_sessions": 0,
    "evidence_artifacts": 2,
    "linked_sessions": 0,
    "plan_files": 0
  },
  "file_ids": ["file_814439bd-4894-4e11-852d-314e9f777a7f"]
}
  • First call with empty file_ids uploads a workspace performance JSON summary + up to 19 artifact files to xAI.
  • If no evidence exists, both modes still return 200 with an empty-data template (report object or chat message).
  • Chat mode: pass returned file_ids on follow-up calls to avoid re-uploading the context bundle.
POST/api/v2/agent/workspaces/{workspace_id}/blocks/{block_id}/ghl-linksghl:write201 Created

Create a private GHL Score link for a block (15 or 30 minutes).

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.
block_iduuidyesTarget block ID.

Request body (JSON)

FieldTypeRequiredDescription
minutesinteger—15 or 30 only; any other value defaults to 15.
guest_user_iduuid—Org admin only: assign link to a guest by ID.
guest_emailstring—Org admin only: assign link to a guest by email.

Request example

{
  "minutes": 15,
  "guest_email": "learner@example.com"
}

Response body

FieldTypeRequiredDescription
ghl_link.iduuid—GHL link / session row ID.
ghl_link.plan_iduuid—Workspace ID.
ghl_link.plan_node_iduuid—Block ID.
ghl_link.statusstring—pending | in_progress | completed
ghl_link.requested_duration_secondsinteger—900 (15 min) or 1800 (30 min).
ghl_link.focus_node_idsuuid[]—Focused block IDs (usually the target block).
ghl_link.created_atISO-8601—Link creation time.
ghl_link.private_urlstring—Bearer URL: /ghl-score/session/{token}. No login required.

Response example

{
  "ghl_link": {
    "id": "ae0cc774-1832-4bb5-bc7d-bf119ddf759f",
    "plan_id": "75b3b4ef-4e47-4f39-bb09-f61406603d75",
    "plan_node_id": "88a43ad8-62f8-4252-a847-2cbc0b754a57",
    "status": "pending",
    "requested_duration_seconds": 900,
    "focus_node_ids": ["88a43ad8-62f8-4252-a847-2cbc0b754a57"],
    "created_at": "2026-06-23T01:29:03.861663+00:00",
    "private_url": "https://openlesson.academy/ghl-score/session/E8-ouJ9lErgDEmteyKc4tJ39meJ91vzZFNUiuRauHvw"
  }
}
  • Guest keys auto-attach the link to their guest identity.
  • Org admins may set guest_user_id or guest_email to assign the link (404 guest_not_found if missing).
  • Learner completes session at private_url without an API key.
GET/api/v2/agent/workspaces/{workspace_id}/ghl-linksghl:read200 OK

List GHL links for a workspace (filtered by caller role).

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.

Response body

FieldTypeRequiredDescription
ghl_linksarray—Sessions ordered by created_at descending.
ghl_links[].iduuid—Link ID.
ghl_links[].plan_iduuid—Workspace ID.
ghl_links[].plan_node_iduuid—Block ID.
ghl_links[].statusstring—pending | in_progress | completed
ghl_links[].requested_duration_secondsinteger—Requested duration.
ghl_links[].duration_secondsinteger—Actual duration (0 until completed).
ghl_links[].focus_node_idsuuid[]—Focused blocks.
ghl_links[].overall_scoreinteger | null—Score when completed.
ghl_links[].created_atISO-8601—Created at.
ghl_links[].started_atISO-8601 | null—Started at.
ghl_links[].completed_atISO-8601 | null—Completed at.

Response example

{
  "ghl_links": [
    {
      "id": "ae0cc774-1832-4bb5-bc7d-bf119ddf759f",
      "plan_id": "75b3b4ef-4e47-4f39-bb09-f61406603d75",
      "plan_node_id": "88a43ad8-62f8-4252-a847-2cbc0b754a57",
      "status": "completed",
      "requested_duration_seconds": 900,
      "duration_seconds": 120,
      "focus_node_ids": ["88a43ad8-62f8-4252-a847-2cbc0b754a57"],
      "overall_score": 72,
      "created_at": "2026-06-23T01:29:03.861663+00:00",
      "started_at": "2026-06-23T01:30:00+00:00",
      "completed_at": "2026-06-23T01:32:21.492+00:00"
    }
  ]
}
  • Guests see only their own links.
  • Non-admin members see only links they created.
  • Org admins see all links on org workspaces.
GET/api/v2/agent/workspaces/{workspace_id}/ghl-links/{link_id}/resultsghl:read200 OK

Poll GHL link completion and read scores, markers, and gap analysis.

Path parameters

FieldTypeRequiredDescription
workspace_iduuidyesPerformance Workspace ID.
link_iduuidyesGHL link ID from create or list.

Response body

FieldTypeRequiredDescription
ghl_result.iduuid—Link ID.
ghl_result.workspace_iduuid—Workspace ID.
ghl_result.block_iduuid—Block ID.
ghl_result.xai_file_idstring | null—xAI artifact file when completed.
ghl_result.statusstring—pending | in_progress | completed
ghl_result.completedboolean—True when status=completed.
ghl_result.duration_secondsinteger—Actual session duration.
ghl_result.requested_duration_secondsinteger—Requested duration.
ghl_result.focus_block_idsuuid[]—Focused blocks.
ghl_result.summarystring | null—Overall reflection when completed.
ghl_result.overall_scoreinteger | null—0–100 when completed.
ghl_result.marker_scoresarray | null—id, label, score, rationale per marker when completed.
ghl_result.gap_analysisobject | null—summary, gaps[], next_practice[] when completed.
ghl_result.analysisobject | null—Full analysis JSON when completed.
ghl_result.created_atISO-8601—Created at.
ghl_result.started_atISO-8601 | null—Started at.
ghl_result.completed_atISO-8601 | null—Completed at.

Response example

{
  "ghl_result": {
    "id": "ae0cc774-1832-4bb5-bc7d-bf119ddf759f",
    "workspace_id": "75b3b4ef-4e47-4f39-bb09-f61406603d75",
    "block_id": "88a43ad8-62f8-4252-a847-2cbc0b754a57",
    "xai_file_id": "file_1f27f79e-81c2-4ef9-9cb5-28c36a5227c4",
    "status": "completed",
    "completed": true,
    "duration_seconds": 120,
    "requested_duration_seconds": 900,
    "focus_block_ids": ["88a43ad8-62f8-4252-a847-2cbc0b754a57"],
    "summary": "Learner demonstrated basic RAG concepts with room to improve causal links.",
    "overall_score": 72,
    "marker_scores": [
      {
        "id": "conceptual_clarity",
        "label": "Conceptual Clarity",
        "score": 78,
        "rationale": "Defined retrieval and generation stages clearly."
      }
    ],
    "gap_analysis": {
      "summary": "Integration between retrieval quality and answer faithfulness needs work.",
      "gaps": [
        {
          "title": "Weak causal link retrieval→accuracy",
          "evidence": "Could not explain how bad retrieval causes hallucinations.",
          "severity": "medium",
          "suggested_repair": "Practice explaining failure modes with a concrete bad-retrieval example."
        }
      ],
      "next_practice": ["Define faithfulness vs fluency tradeoff"]
    },
    "analysis": { },
    "created_at": "2026-06-23T01:29:03.861663+00:00",
    "started_at": "2026-06-23T01:30:00+00:00",
    "completed_at": "2026-06-23T01:32:21.492+00:00"
  }
}
  • Pending/in_progress: summary, overall_score, marker_scores, gap_analysis, and analysis are null.
  • 404 ghl_link_not_found if link does not exist or caller cannot access it.
POST/api/v2/agent/org/guestsorg:write201 Created (new guest) or 200 OK (existing guest)

Create or look up a guest by email and issue a new guest API key (gsk_).

Request body (JSON)

FieldTypeRequiredDescription
emailstringyesGuest email address (normalized to lowercase).

Request example

{ "email": "learner@example.com" }

Response body

FieldTypeRequiredDescription
guest_user.iduuid—organization_guest_users.id
guest_user.organization_iduuid—Org the guest belongs to.
guest_user.emailstring—Guest email.
guest_user.statusstring—active | claimed | revoked
guest_user.claimed_by_user_iduuid | null—Set when guest signs up with same email.
guest_user.claimed_atISO-8601 | null—Claim timestamp.
guest_user.created_atISO-8601—Guest record created at.
api_keystring—Raw gsk_ key — shown once; store securely.
key.iduuid—agent_api_keys.id
key.key_prefixstring—First 13 chars of key for identification.
key.scopesstring[]—workspaces:read, workspaces:write, ghl:read, ghl:write
key.rate_limitinteger—Requests per minute (default 120).
key.created_atISO-8601—Key creation time.

Response example

{
  "guest_user": {
    "id": "f8b2c1d0-1234-5678-9abc-def012345678",
    "organization_id": "64cc093b-31c1-4a7e-aead-e2e9378ecaf4",
    "email": "learner@example.com",
    "status": "active",
    "claimed_by_user_id": null,
    "claimed_at": null,
    "created_at": "2026-06-23T13:00:00+00:00"
  },
  "api_key": "gsk_a1b2c3d4e5f6789012345678abcdef",
  "key": {
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "key_prefix": "gsk_a1b2c3d4",
    "scopes": ["workspaces:read", "workspaces:write", "ghl:read", "ghl:write"],
    "rate_limit": 120,
    "created_at": "2026-06-23T13:00:01+00:00"
  }
}
  • Caller must be organization admin with org:write on their sk_ key.
  • Re-calling for the same email mints another key; prior keys may remain active.
  • 409 if email belongs to a real user in another organization.
GET/api/v2/agent/keysbrowser session200 OK

List API keys for the signed-in dashboard user. Uses Supabase session cookies — not Bearer API key auth.

Response body

FieldTypeRequiredDescription
keysarray—All agent_api_keys for the authenticated user, newest first.
keys[].iduuid—Key ID.
keys[].labelstring | null—Optional label set at creation.
keys[].key_prefixstring—First 12 characters of sk_ key (identification only).
keys[].scopesstring[]—Assigned scopes.
keys[].rate_limitinteger—Requests per minute (default 120).
keys[].is_activeboolean—False after revocation.
keys[].created_atISO-8601—Creation timestamp.
keys[].last_used_atISO-8601 | null—Last successful API call.
keys[].expires_atISO-8601 | null—Expiry if set at creation.

Response example

{
  "keys": [
    {
      "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
      "label": "Production agent",
      "key_prefix": "sk_a1b2c3d4e5",
      "scopes": ["workspaces:read", "workspaces:write", "ghl:read", "ghl:write"],
      "rate_limit": 120,
      "is_active": true,
      "created_at": "2026-06-23T12:00:00+00:00",
      "last_used_at": "2026-06-23T13:05:00+00:00",
      "expires_at": null
    }
  ]
}
  • Also available from Dashboard → Usage & API. Max 10 active keys per user.
POST/api/v2/agent/keysbrowser session201 Created

Create a new sk_ API key for the signed-in user. Raw key returned once.

Request body (JSON)

FieldTypeRequiredDescription
labelstring—Optional label, max 128 characters.
scopesstring[]—Optional. Default: workspaces:read, workspaces:write, ghl:read, ghl:write. org:read/org:write require org admin.
expires_in_daysinteger—Optional expiry: 1–365 days.

Request example

{
  "label": "CI pipeline",
  "scopes": ["workspaces:read", "workspaces:write", "ghl:read", "ghl:write", "org:write"],
  "expires_in_days": 90
}

Response body

FieldTypeRequiredDescription
key.iduuid—agent_api_keys.id
key.labelstring | null—Label if provided.
key.key_prefixstring—First 12 chars of sk_ key.
key.scopesstring[]—Assigned scopes.
key.rate_limitinteger—Default 120.
key.created_atISO-8601—Creation time.
key.expires_atISO-8601 | null—Expiry if set.
api_keystring—Full sk_ key — shown once; store securely.

Response example

{
  "key": {
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "label": "CI pipeline",
    "key_prefix": "sk_a1b2c3d4e5",
    "scopes": ["workspaces:read", "workspaces:write", "ghl:read", "ghl:write", "org:write"],
    "rate_limit": 120,
    "created_at": "2026-06-23T12:00:00+00:00",
    "expires_at": "2026-09-21T12:00:00+00:00"
  },
  "api_key": "sk_7f3a9b2c1d4e5f6789012345678abcdef"
}
  • Requires Teams tier (403 teams_required).
  • Valid scopes: *, workspaces:read, workspaces:write, ghl:read, ghl:write, org:read, org:write.
  • 403 if more than 10 active keys or if non-admin requests org scopes.
DELETE/api/v2/agent/keys/{key_id}browser session200 OK

Revoke (soft-delete) an API key owned by the signed-in user.

Path parameters

FieldTypeRequiredDescription
key_iduuidyesagent_api_keys.id from list or create.

Response body

FieldTypeRequiredDescription
deletedboolean—True on success.
key_iduuid—Revoked key ID.

Response example

{
  "deleted": true,
  "key_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
}
  • 404 not_found if key does not belong to user. 400 if already revoked.
PATCH/api/v2/agent/keys/{key_id}/scopesbrowser session200 OK

Replace scopes on an active API key.

Path parameters

FieldTypeRequiredDescription
key_iduuidyesagent_api_keys.id.

Request body (JSON)

FieldTypeRequiredDescription
scopesstring[]yesNon-empty array of valid scope strings.

Request example

{
  "scopes": ["workspaces:read", "ghl:read"]
}

Response body

FieldTypeRequiredDescription
key.iduuid—Updated key ID.
key.scopesstring[]—New scope list.
key.updated_atISO-8601—Update timestamp.

Response example

{
  "key": {
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "scopes": ["workspaces:read", "ghl:read"],
    "updated_at": "2026-06-23T13:10:00+00:00"
  }
}
  • Cannot update revoked keys. org:read/org:write require organization admin.

GHL session completion (learner-facing)

Learners open private_url without an API key. Completion uses web APIs (not Agentic API):

POST /api/workspace-ghl-score/chat

FieldTypeRequiredDescription
privateTokenstringyesToken from private_url path.
thoughtstringyesLearner thought fragment.
messagesarray—Optional prior chat messages.

POST /api/workspace-ghl-score/complete

FieldTypeRequiredDescription
privateTokenstringyesToken from private_url path.
transcriptarrayyesSession transcript entries with role and text.
durationSecondsinteger—Elapsed session seconds.

For agents

Machine-readable spec

Agents should also load /skill.md for integration checklists, guest responsibilities, and MCP transport. PumaDoc evidence integration: /pumadoc-evidence-performance-skill.md.

openLesson

Performance readiness for AI-enabled work. Workspaces, immersive learning, evaluation, and API integration.

Product

  • Platform
  • Pricing
  • Agentic API

Solutions

  • Sales Enablement
  • Customer Success
  • Compliance & Risk
  • Hiring & Assessment
  • LMS Integration
  • Engineering & On-Call
  • Corporate L&D

Resources

  • Agent skill file
  • GitHub

Legal

  • Privacy
  • Terms
  • Cookies
  • Legal Notice
@uncertainsysdaniel@uncertain.systems

© 2026 Uncertain Systems (Daniel Colomer). All rights reserved.

Building the open stack for educational technology