Skip to main content
GET
/
public
/
sessions
/
{access_token}
Public Sessions
curl --request GET \
  --url https://api.example.com/public/sessions/{access_token}
Public endpoints for end users to access and interact with KYC onboarding sessions. These endpoints require no authentication - access is controlled via secure access tokens.

Overview

Public session endpoints allow end users to:
  • Access Sessions: View session state and workflow definition
  • Upload Documents: Upload required documents, selfies, and file fields directly to S3
  • Extract Data: Extract data from uploaded documents
  • Complete Steps: Mark steps as completed with form data
  • Complete Liveness: Start and verify AWS Rekognition Face Liveness checks
  • Manage Subsessions: Create and track related-party verification sessions
  • Resolve Corrections: Fix reviewer-requested remediation items using the same access token
These endpoints are designed for frontend integration and require no authentication - access is controlled via the access token provided when creating the session.

Endpoints

MethodEndpointDescription
GET/public/sessions/{access_token}Get session state
GET/public/sessions/{access_token}/workflowGet workflow definition
POST/public/sessions/{access_token}/upload/initInitialize direct S3 upload
PUT{upload_url}Upload file bytes directly to S3
POST/public/sessions/{access_token}/upload/confirmConfirm direct S3 upload
POST/public/sessions/{access_token}/uploadLegacy base64 upload
POST/public/sessions/{access_token}/documentsLegacy base64 upload alias
POST/public/sessions/{access_token}/extractExtract document data
POST/public/sessions/{access_token}/step/{step_id}/completeComplete a step
POST/public/sessions/{access_token}/liveness/startStart a liveness check
POST/public/sessions/{access_token}/liveness/completeComplete a liveness check
POST/public/sessions/{access_token}/subsessionsCreate a subsession
GET/public/sessions/{access_token}/subsessionsList subsessions, optionally filtered by step_id
POST/public/sessions/{access_token}/subsessions/{subsession_id}/regenerateRegenerate subsession link
DELETE/public/sessions/{access_token}/subsessions/{subsession_id}Delete an unfinished subsession

Authentication

No authentication required. Access is controlled via the access_token path parameter.

Get Session State

Retrieve the current state of a session including progress and uploaded documents.

Endpoint

GET /public/sessions/{access_token}

Request Example

curl https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002

Response Format

{
  "status": "success",
  "data": {
    "session_id": "660e8400-e29b-41d4-a716-446655440001",
    "workflow_name": "Basic KYC",
    "status": "active",
    "current_step_index": 1,
    "total_steps": 3,
    "steps_data": [
      {
        "step_id": "step_1",
        "status": "completed",
        "started_at": "2024-11-22T10:31:00Z",
        "completed_at": "2024-11-22T10:32:00Z",
        "documents": [
          {
            "doc_id": "doc_abc123",
            "type": "id",
            "file_name": "passport.jpg",
            "mime_type": "image/jpeg",
            "uploaded_at": "2024-11-22T10:32:00Z"
          }
        ]
      },
      {
        "step_id": "step_2",
        "status": "in_progress",
        "started_at": "2024-11-22T10:33:00Z",
        "liveness_sessions": [
          {
            "liveness_session_id": "liv_abc123",
            "status": "pending",
            "created_at": "2024-11-22T10:33:00Z"
          }
        ]
      },
      {
        "step_id": "step_3",
        "status": "pending"
      }
    ],
    "expires_at": 1732278600,
    "created_at": "2024-11-22T10:30:00Z",
    "branding": {
      "logo_url": "https://...",
      "primary_color": "#10B981",
      "secondary_color": "#064E3B",
      "background_color": "#FFFFFF",
      "text_color": "#1F2937",
      "accent_color": "#34D399"
    }
  }
}
steps_data is sanitized for public clients. Internal storage keys such as S3 object paths, extraction profiles, and internal validation metadata are not returned. When a session is in awaiting_client_correction, only the steps with open correction_requests can be edited. Correction requests expose customer-facing guidance such as message, field_ids, and document_types; reviewer identity and internal audit fields are not returned.

Get Workflow Definition

Retrieve the workflow definition to know what steps and fields are required.

Endpoint

GET /public/sessions/{access_token}/workflow

Request Example

curl https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/workflow

Response Format

{
  "status": "success",
  "data": {
    "workflow_name": "Basic KYC",
    "description": "Standard KYC onboarding workflow",
    "total_steps": 3,
    "steps": [
      {
        "step_id": "step_1",
        "order": 1,
        "name": "ID Document",
        "type": "document_upload",
        "required": true,
        "required_documents": ["id"],
        "document_requirements": [
          {
            "document_type": "id",
            "label": "Government ID",
            "required": true,
            "allow_multiple": false
          }
        ],
        "custom_fields": [],
        "fields": []
      },
      {
        "step_id": "step_2",
        "order": 2,
        "name": "Liveness",
        "type": "liveness",
        "required": true,
        "required_documents": [],
        "document_requirements": [],
        "custom_fields": [],
        "fields": [],
        "liveness_config": {
          "min_confidence": 90
        }
      },
      {
        "step_id": "step_3",
        "order": 3,
        "name": "Personal Information",
        "type": "form_fill",
        "required": true,
        "required_documents": [],
        "document_requirements": [],
        "custom_fields": [
          {
            "field_id": "full_name",
            "name": "Full Name",
            "type": "text",
            "required": true
          },
          {
            "field_id": "date_of_birth",
            "name": "Date of Birth",
            "type": "date",
            "required": true
          },
          {
            "field_id": "email",
            "name": "Email",
            "type": "email",
            "required": true
          }
        ],
        "fields": []
      }
    ],
    "completion_redirect_url": "https://example.com/kyc-complete"
  }
}
Each step can also include optional display and behavior fields such as description, instructions, tooltip, document_types, conditional_logic, and subsession_config when configured on the workflow.

Upload Files

The current public-session frontend uploads files directly to S3 using presigned URLs. This is the recommended and default flow for documents, selfies, and file fields because the file bytes never pass through the API Gateway/Lambda JSON body. The upload has three steps:
  1. Call /upload/init with file metadata and the workflow step context.
  2. Upload the raw file bytes to the returned upload_url using HTTP PUT.
  3. Call /upload/confirm so the API registers the uploaded S3 object in the session.
Use the legacy base64 upload only if you are maintaining an older integration that cannot upload directly to S3.

Initialize Upload

Endpoint

POST /public/sessions/{access_token}/upload/init

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/upload/init \
  -H "Content-Type: application/json" \
  -d '{
    "step_id": "step_1",
    "document_type": "id",
    "file_name": "passport.pdf",
    "mime_type": "application/pdf"
  }'

Response Format

{
  "status": "success",
  "data": {
    "doc_id": "doc_abc123",
    "upload_url": "https://...",
    "s3_key": "tenant123/660e8400-e29b-41d4-a716-446655440001/doc_abc123.pdf",
    "expires_at": 1732278600
  }
}

Upload To S3

Upload the file bytes to upload_url with an HTTP PUT. Use the same Content-Type passed to /upload/init.

Request Example

curl -X PUT "<upload_url>" \
  -H "Content-Type: application/pdf" \
  --data-binary "@passport.pdf"
Do not send this request to the KYC API host. upload_url is a presigned S3 URL returned by /upload/init.

Confirm Upload

Endpoint

POST /public/sessions/{access_token}/upload/confirm

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/upload/confirm \
  -H "Content-Type: application/json" \
  -d '{
    "doc_id": "doc_abc123",
    "s3_key": "tenant123/660e8400-e29b-41d4-a716-446655440001/doc_abc123.pdf",
    "step_id": "step_1",
    "document_type": "id",
    "file_name": "passport.pdf",
    "mime_type": "application/pdf"
  }'

Response Format

{
  "status": "success",
  "data": {
    "doc_id": "doc_abc123",
    "s3_key": "tenant123/660e8400-e29b-41d4-a716-446655440001/doc_abc123.pdf",
    "uploaded_at": "2024-11-22T10:32:00Z",
    "confirmed": true
  }
}
After confirmation, use doc_id when extracting data or completing form file fields.

Legacy Base64 Upload

The base64 upload endpoint remains available for backward compatibility, but new integrations should use direct S3 uploads via /upload/init and /upload/confirm.

Endpoint

POST /public/sessions/{access_token}/upload
POST /public/sessions/{access_token}/documents is also supported as a legacy alias.

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/upload \
  -H "Content-Type: application/json" \
  -d '{
    "step_id": "step_1",
    "document_type": "id",
    "file_data": "base64_encoded_file_data...",
    "file_name": "passport.jpg",
    "mime_type": "image/jpeg"
  }'

Response Format

{
  "status": "success",
  "data": {
    "doc_id": "doc_abc123",
    "s3_key": "tenant123/660e8400-e29b-41d4-a716-446655440001/doc_abc123.jpg",
    "uploaded_at": "2024-11-22T10:32:00Z"
  }
}

Extract Document Data

Extract structured data from an uploaded document using OCR/AI.

Endpoint

POST /public/sessions/{access_token}/extract

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/extract \
  -H "Content-Type: application/json" \
  -d '{
    "doc_id": "doc_abc123",
    "document_type": "passport"
  }'

Response Format

{
  "status": "success",
  "data": {
    "doc_id": "doc_abc123",
    "extracted_data": {
      "full_name": "JOHN DOE",
      "document_number": "AB1234567",
      "date_of_birth": "1985-03-15",
      "nationality": "US",
      "expiry_date": "2030-01-15",
      "gender": "M"
    },
    "confidence_scores": {
      "overall": 0.95,
      "full_name": 0.98,
      "document_number": 0.99,
      "date_of_birth": 0.92
    }
  }
}

Complete Step

Mark a step as completed, optionally with form data.

Endpoint

POST /public/sessions/{access_token}/step/{step_id}/complete

Path Parameters

Request Body Parameters

Request Example - Document Step

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/step/step_1/complete \
  -H "Content-Type: application/json" \
  -d '{}'

Request Example - Form Step

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/step/step_3/complete \
  -H "Content-Type: application/json" \
  -d '{
    "form_data": {
      "full_name": "John Doe",
      "date_of_birth": "1985-03-15",
      "email": "john@example.com"
    }
  }'

Response Format

{
  "status": "success",
  "data": {
    "step_id": "step_3",
    "status": "completed",
    "next_step_id": null,
    "session_completed": true
  }
}

Response Fields

FieldDescription
step_idThe completed step ID
statusStep status after completion
next_step_idID of the next step, or null if this was the last step
session_completedTrue if all steps are now completed

Correction Mode

If a reviewer requests corrections, the session status becomes awaiting_client_correction and one or more steps become needs_correction. The end user keeps using the original public link; no new token is required.

Public State Example

{
  "status": "success",
  "data": {
    "session_id": "660e8400-e29b-41d4-a716-446655440001",
    "status": "awaiting_client_correction",
    "current_step_index": 1,
    "steps_data": [
      {
        "step_id": "id_document",
        "status": "completed"
      },
      {
        "step_id": "personal_info",
        "status": "needs_correction",
        "form_data": {
          "full_name": "Jon Doe"
        },
        "correction_requests": [
          {
            "id": "corr_abc123",
            "step_id": "personal_info",
            "field_ids": ["full_name"],
            "document_types": [],
            "message": "Please correct your legal name so it matches your ID.",
            "status": "open",
            "created_at": "2026-05-22T14:30:00Z"
          }
        ]
      }
    ]
  }
}

Resolving Corrections

To resolve a correction, update the requested fields or documents and call the regular step completion endpoint:
curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/step/personal_info/complete \
  -H "Content-Type: application/json" \
  -d '{
    "form_data": {
      "full_name": "John Doe"
    }
  }'
After a corrected step is completed, its open correction requests are marked resolved. If other steps still have open corrections, the session remains awaiting_client_correction; otherwise the next status depends on the workflow’s validation_config.correction_resolution setting:
ModeResult
manual_reviewSession returns to manual_review for a reviewer decision
auto_processSession moves to completed and processing is triggered
completedSession moves to completed without automatic processing

Liveness Checks

Liveness steps use AWS Rekognition Face Liveness. The frontend starts a liveness session, passes the returned liveness_session_id to the AWS Amplify Liveness SDK, then confirms the result.

Start Liveness

Endpoint

POST /public/sessions/{access_token}/liveness/start

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/liveness/start \
  -H "Content-Type: application/json" \
  -d '{
    "step_id": "step_2"
  }'

Response Format

{
  "status": "success",
  "data": {
    "liveness_session_id": "liv_abc123",
    "step_id": "step_2",
    "message": "Use this session ID with the AWS Amplify Liveness SDK"
  }
}

Complete Liveness

Endpoint

POST /public/sessions/{access_token}/liveness/complete

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/liveness/complete \
  -H "Content-Type: application/json" \
  -d '{
    "liveness_session_id": "liv_abc123"
  }'

Response Format

{
  "status": "success",
  "data": {
    "passed": true,
    "confidence": 99.2,
    "reference_image_s3_key": "tenant123/660e8400-e29b-41d4-a716-446655440001/liveness.jpg",
    "step_completed": true,
    "session_completed": false,
    "next_step_id": "step_3",
    "message": "Liveness check passed"
  }
}

Error Responses

404 Not Found - Invalid Access Token

{
  "status": "error",
  "error": {
    "code": "NOT_FOUND",
    "message": "Session not found or expired"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

400 Bad Request - Invalid Step

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Step not found"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

400 Bad Request - Invalid Document Type

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Document type not allowed for this step"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

400 Bad Request - Missing Required Fields

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Campos requeridos: missing required field"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

Status Codes

CodeDescription
200Success
201Created - presigned upload initialized, liveness session started, or subsession created
400Bad Request - Invalid parameters or validation failed
403Forbidden - operation not allowed
404Not Found - Invalid access token or step not found
500Internal Server Error

JavaScript Integration Example

const accessToken = "770e8400-e29b-41d4-a716-446655440002";
const baseUrl = "https://kyc.legaltalent.ai/public/sessions";

// Get session state
async function getSessionState() {
  const response = await fetch(`${baseUrl}/${accessToken}`);
  const data = await response.json();
  return data.data;
}

// Get workflow definition
async function getWorkflow() {
  const response = await fetch(`${baseUrl}/${accessToken}/workflow`);
  const data = await response.json();
  return data.data;
}

// Upload document directly to S3 using the current presigned upload flow
async function uploadDocument(file, stepId, documentType) {
  const initResponse = await fetch(`${baseUrl}/${accessToken}/upload/init`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      step_id: stepId,
      document_type: documentType,
      file_name: file.name,
      mime_type: file.type
    })
  });
  const initData = await initResponse.json();

  await fetch(initData.data.upload_url, {
    method: "PUT",
    headers: { "Content-Type": file.type },
    body: file
  });

  const confirmResponse = await fetch(`${baseUrl}/${accessToken}/upload/confirm`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      doc_id: initData.data.doc_id,
      s3_key: initData.data.s3_key,
      step_id: stepId,
      document_type: documentType,
      file_name: file.name,
      mime_type: file.type
    })
  });
  const confirmData = await confirmResponse.json();
  return confirmData.data;
}

// Legacy base64 upload. Prefer uploadDocument() above for new integrations.
async function uploadDocumentLegacyBase64(file, stepId, documentType) {
  const base64Data = await new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result.split(",")[1]);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });

  const response = await fetch(`${baseUrl}/${accessToken}/upload`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      step_id: stepId,
      document_type: documentType,
      file_data: base64Data,
      file_name: file.name,
      mime_type: file.type
    })
  });

  const data = await response.json();
  return data.data;
}

// Extract data from document
async function extractDocument(docId, documentType) {
  const response = await fetch(`${baseUrl}/${accessToken}/extract`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ 
      doc_id: docId,
      document_type: documentType 
    })
  });
  
  const data = await response.json();
  return data.data;
}

// Complete a step
async function completeStep(stepId, formData = null) {
  const response = await fetch(
    `${baseUrl}/${accessToken}/step/${stepId}/complete`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ form_data: formData })
    }
  );
  
  const data = await response.json();
  return data.data;
}

// Example: Complete onboarding flow
async function runOnboardingFlow() {
  // 1. Get workflow to know the steps
  const workflow = await getWorkflow();
  console.log("Workflow:", workflow.workflow_name);
  
  // 2. For each step, complete it
  for (const step of workflow.steps) {
    console.log(`Processing step: ${step.name}`);
    
    if (step.type === 'document_upload') {
      // Upload required documents
      // In real app: get file from user input
    } else if (step.type === 'selfie') {
      // Capture and upload selfie
    } else if (step.type === 'form_fill') {
      // Collect form data from user
      const formData = {
        full_name: "John Doe",
        date_of_birth: "1985-03-15"
      };
      await completeStep(step.step_id, formData);
    }
  }
  
  // 3. Check final state
  const session = await getSessionState();
  console.log("Session status:", session.status);
}

Subsessions

Subsessions allow end users to create verification sessions for related parties (UBOs, submerchants, directors) during the main onboarding process. These are available when the workflow includes a subsession_collection step type.

Create Subsession

Create a new subsession for a related party (e.g., UBO, submerchant).

Endpoint

POST /public/sessions/{access_token}/subsessions

Request Body Parameters

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/subsessions \
  -H "Content-Type: application/json" \
  -d '{
    "step_id": "step_ubos",
    "label": "Juan Garcia - UBO",
    "metadata": {
      "ownership_percentage": 25
    }
  }'

Response Format

{
  "status": "success",
  "data": {
    "subsession_id": "880e8400-e29b-41d4-a716-446655440003",
    "access_link": "https://kyc.legaltalent.ai/public/sessions/990e8400-e29b-41d4-a716-446655440004",
    "workflow_name": "UBO Verification",
    "label": "Juan Garcia - UBO",
    "expires_at": 1732278600,
    "created_at": "2024-11-22T10:30:00Z"
  }
}

List Subsessions

Get all subsessions created from the current session.

Endpoint

GET /public/sessions/{access_token}/subsessions

Request Example

curl https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/subsessions

Response Format

{
  "status": "success",
  "data": {
    "subsessions": [
      {
        "subsession_id": "880e8400-e29b-41d4-a716-446655440003",
        "access_link": "https://kyc.legaltalent.ai/public/sessions/990e8400-...",
        "status": "completed",
        "label": "Juan Garcia - UBO",
        "tag": "ubo",
        "workflow_id": "ubo-verification-workflow",
        "expires_at": 1732278600,
        "created_at": "2024-11-22T10:30:00Z",
        "completed_at": "2024-11-22T11:00:00Z"
      },
      {
        "subsession_id": "880e8400-e29b-41d4-a716-446655440005",
        "access_link": "https://kyc.legaltalent.ai/public/sessions/990e8400-...",
        "status": "active",
        "label": "Maria Lopez - UBO",
        "tag": "ubo",
        "workflow_id": "ubo-verification-workflow",
        "expires_at": 1732278600,
        "created_at": "2024-11-22T10:35:00Z"
      }
    ],
    "total": 2,
    "completed": 1,
    "pending": 1,
    "min_required": 1,
    "max_allowed": 5,
    "can_proceed": true,
    "category": "ubo"
  }
}
Add ?step_id={step_id} to list only subsessions created for one subsession_collection step. Generate a new access link for a subsession if the original was lost or needs to be refreshed.

Endpoint

POST /public/sessions/{access_token}/subsessions/{subsession_id}/regenerate

Request Example

curl -X POST https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/subsessions/880e8400-e29b-41d4-a716-446655440003/regenerate

Response Format

{
  "status": "success",
  "data": {
    "subsession_id": "880e8400-e29b-41d4-a716-446655440003",
    "access_link": "https://kyc.legaltalent.ai/public/sessions/aa0e8400-e29b-41d4-a716-446655440008",
    "expires_at": 1732278600
  }
}

Delete Subsession

Delete an unfinished subsession from the parent session. Completed, processed, or approved subsessions cannot be deleted.

Endpoint

DELETE /public/sessions/{access_token}/subsessions/{subsession_id}

Request Example

curl -X DELETE https://kyc.legaltalent.ai/public/sessions/770e8400-e29b-41d4-a716-446655440002/subsessions/880e8400-e29b-41d4-a716-446655440003

Response Format

{
  "status": "success",
  "data": {
    "subsession_id": "880e8400-e29b-41d4-a716-446655440003",
    "deleted": true
  }
}

Subsession Errors

400 Bad Request - Maximum Subsessions Reached

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Maximum number of subsessions (5) reached"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

400 Bad Request - Invalid Step Type

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Subsession collection step step_1 not found"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

404 Not Found - Subsession Not Found

{
  "status": "error",
  "error": {
    "code": "NOT_FOUND",
    "message": "Subsession not found"
  },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-03-22T12:00:00Z"
  }
}

JavaScript Integration Example - Subsessions

const accessToken = "770e8400-e29b-41d4-a716-446655440002";
const baseUrl = "https://kyc.legaltalent.ai/public/sessions";

// Create a subsession for a UBO
async function createSubsession(stepId, label, metadata = {}) {
  const response = await fetch(`${baseUrl}/${accessToken}/subsessions`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      step_id: stepId,
      label: label,
      metadata: metadata
    })
  });
  
  const data = await response.json();
  if (data.status === 'success') {
    console.log("Subsession link:", data.data.access_link);
    return data.data;
  } else {
    throw new Error(data.error);
  }
}

// List all subsessions for the current session
async function listSubsessions(stepId = null) {
  const query = stepId ? `?step_id=${encodeURIComponent(stepId)}` : "";
  const response = await fetch(`${baseUrl}/${accessToken}/subsessions${query}`);
  const data = await response.json();
  return data.data;
}

// Regenerate subsession link
async function regenerateSubsessionLink(subsessionId) {
  const response = await fetch(
    `${baseUrl}/${accessToken}/subsessions/${subsessionId}/regenerate`,
    { method: "POST" }
  );
  
  const data = await response.json();
  return data.data;
}

// Example: Create subsessions for UBOs
async function handleUBOStep(stepId) {
  const ubos = [
    { name: "Juan Garcia", ownership: 25 },
    { name: "Maria Lopez", ownership: 35 },
    { name: "Carlos Ruiz", ownership: 40 }
  ];
  
  const subsessionLinks = [];
  
  for (const ubo of ubos) {
    const subsession = await createSubsession(
      stepId,
      `${ubo.name} - UBO`,
      { ownership_percentage: ubo.ownership }
    );
    
    subsessionLinks.push({
      name: ubo.name,
      link: subsession.access_link
    });
    
    console.log(`Created subsession for ${ubo.name}: ${subsession.access_link}`);
  }
  
  // Return links to share with UBOs
  return subsessionLinks;
}

Best Practices

  • Security: Access tokens are UUIDs - share them securely with end users
  • Expiration: Check expires_at before allowing user actions
  • Error Handling: Handle expired sessions gracefully with user-friendly messages
  • File Validation: Validate file types and sizes client-side before upload
  • File Uploads: Use direct S3 uploads (/upload/init → S3 PUT/upload/confirm) for new integrations
  • Legacy Uploads: Use base64 /upload only for old clients that cannot upload directly to S3
  • Progress Tracking: Use current_step_index and total_steps to show progress
  • Step Validation: The API validates required fields - handle validation errors in UI
  • Correction Mode: When status is awaiting_client_correction, show each step’s open correction_requests and allow edits only for those steps
  • Subsession Management: For subsession_collection steps, check max_allowed before creating new ones
  • Subsession Completion: If require_completion_before_proceed is enabled on the step, all subsessions must be completed before the parent session can proceed