> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydocs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Raydocs Client

> Reusable API client implementations in TypeScript, Python, and PHP

This page provides ready-to-use API client implementations that you can copy into your projects. These clients are used throughout the cookbook examples.

<Tabs>
  <Tab title="TypeScript">
    ## TypeScript / Node.js Client

    ```typescript theme={null}
    import axios, { AxiosInstance } from "axios";
    import * as fs from "fs";

    interface Workspace {
        id: number;
        name: string;
        icon: string;
    }

    interface Template {
        id: string;
        name: string;
        description?: string;
        schema_json: object;
    }

    interface Session {
        id: string;
        name: string;
        created_at: string;
    }

    interface ExtractionResult {
        id: string;
        status: "created" | "parsing_pending" | "processing" | "completed" | "failed";
        data: Record<string, any>;
        reasoning?: Record<string, any>;
    }

    export class RaydocsClient {
        private client: AxiosInstance;

        constructor(apiKey: string, baseUrl: string = "https://api.raydocs.com") {
            this.client = axios.create({
                baseURL: baseUrl,
                headers: {
                    Authorization: `Bearer ${apiKey}`,
                    "Content-Type": "application/json",
                    Accept: "application/json",
                },
            });
        }

        // ─────────────────────────────────────────────────────────────
        // Workspaces
        // ─────────────────────────────────────────────────────────────

        async listWorkspaces(): Promise<Workspace[]> {
            const { data } = await this.client.get("/workspaces");
            return data.data;
        }

        async createWorkspace(
            name: string,
            icon: string = "📊"
        ): Promise<Workspace> {
            const { data } = await this.client.post("/workspaces/create", {
                name,
                icon,
            });
            return data;
        }

        // ─────────────────────────────────────────────────────────────
        // Templates
        // ─────────────────────────────────────────────────────────────

        async listTemplates(workspaceId: number): Promise<Template[]> {
            const { data } = await this.client.get(
                `/workspaces/${workspaceId}/extractions/templates`
            );
            return data.data;
        }

        async createTemplate(
            workspaceId: number,
            name: string,
            schema: object,
            description?: string
        ): Promise<Template> {
            const { data } = await this.client.post("/extractions/templates", {
                workspace_id: workspaceId,
                name,
                schema_json: schema,
                description,
            });
            return data;
        }

        async getTemplate(templateId: string): Promise<Template> {
            const { data } = await this.client.get(
                `/extractions/templates/${templateId}`
            );
            return data;
        }

        // ─────────────────────────────────────────────────────────────
        // File Upload
        // ─────────────────────────────────────────────────────────────

        async uploadFile(filePath: string): Promise<string> {
            // Get signed upload URL (content_type defaults to 'application/octet-stream')
            const { data: uploadData } = await this.client.post(
                "/vapor/signed-storage-url",
                {
                    visibility: "private",
                }
            );

            // Upload directly to S3
            const fileBuffer = fs.readFileSync(filePath);
            await axios.put(uploadData.url, fileBuffer, {
                headers: uploadData.headers,
            });

            return uploadData.key;
        }

        // ─────────────────────────────────────────────────────────────
        // Batch Operations
        // ─────────────────────────────────────────────────────────────

        async batchCreateSessions(
            templateId: string,
            fileKeys: string[],
            autoExtract: boolean = true
        ): Promise<Session[]> {
            const { data } = await this.client.post(
                `/extractions/templates/${templateId}/sessions/batch`,
                {
                    files: fileKeys,
                    settings: { auto_extract: autoExtract },
                }
            );
            return data;
        }

        // ─────────────────────────────────────────────────────────────
        // Sessions & Results
        // ─────────────────────────────────────────────────────────────

        async getSession(sessionId: string): Promise<any> {
            const { data } = await this.client.get(
                `/extractions/sessions/${sessionId}`
            );
            return data;
        }

        async getResults(sessionId: string): Promise<ExtractionResult[]> {
            const { data } = await this.client.get(
                `/extractions/sessions/${sessionId}/results`
            );
            return data.data;
        }

        async getResult(resultId: string): Promise<ExtractionResult> {
            const { data } = await this.client.get(
                `/extractions/results/${resultId}`
            );
            return data;
        }

        // ─────────────────────────────────────────────────────────────
        // Workspace Documents
        // ─────────────────────────────────────────────────────────────

        async listWorkspaceDocuments(
            workspaceId: number,
            params?: { query?: string; status?: string; page?: number; per_page?: number }
        ): Promise<{ data: any[]; meta: any }> {
            const { data } = await this.client.get(
                `/workspaces/${workspaceId}/documents`,
                { params }
            );
            return data;
        }

        async getWorkspaceDocument(
            workspaceId: number,
            documentId: string
        ): Promise<any> {
            const { data } = await this.client.get(
                `/workspaces/${workspaceId}/documents/${documentId}`
            );
            return data;
        }

        async createWorkspaceDocument(
            workspaceId: number,
            key: string,
            filename?: string
        ): Promise<any> {
            const { data } = await this.client.post(
                `/workspaces/${workspaceId}/documents`,
                { key, filename }
            );
            return data;
        }

        async importWorkspaceDocumentUrl(
            workspaceId: number,
            url: string,
            filename?: string
        ): Promise<any> {
            const { data } = await this.client.post(
                `/workspaces/${workspaceId}/documents:importUrl`,
                { url, filename }
            );
            return data;
        }

        async reparseWorkspaceDocument(
            workspaceId: number,
            documentId: string,
            configHash: string,
            force?: boolean
        ): Promise<any> {
            const { data } = await this.client.post(
                `/workspaces/${workspaceId}/documents/${documentId}/reparse`,
                { config_hash: configHash, force }
            );
            return data;
        }
    }
    ```

    ### Installation

    ```bash theme={null}
    npm install axios
    ```

    Save the client as `raydocs-client.ts`:

    ```typescript theme={null}
    import { RaydocsClient } from "./raydocs-client";

    const client = new RaydocsClient("your_api_token");
    ```
  </Tab>

  <Tab title="Python">
    ## Python Client

    ```python theme={null}
    import requests
    import os
    from typing import Optional, List, Dict, Any

    class RaydocsClient:
        """
        Raydocs API client for document extraction.

        Usage:
            client = RaydocsClient("your_api_token")
            workspaces = client.list_workspaces()
        """

        def __init__(self, api_key: str, base_url: str = "https://api.raydocs.com"):
            self.base_url = base_url
            self.headers = {
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
                "Accept": "application/json"
            }

        def _request(self, method: str, endpoint: str, **kwargs) -> Any:
            """Make an API request with error handling."""
            url = f"{self.base_url}{endpoint}"
            response = requests.request(method, url, headers=self.headers, **kwargs)
            response.raise_for_status()
            return response.json() if response.content else {}

        # ─────────────────────────────────────────────────────────────
        # Workspaces
        # ─────────────────────────────────────────────────────────────

        def list_workspaces(self) -> List[Dict]:
            """List all accessible workspaces."""
            return self._request("GET", "/workspaces")["data"]

        def create_workspace(self, name: str, icon: str = "📊") -> Dict:
            """Create a new workspace."""
            return self._request("POST", "/workspaces/create", json={
                "name": name,
                "icon": icon
            })

        # ─────────────────────────────────────────────────────────────
        # Templates
        # ─────────────────────────────────────────────────────────────

        def list_templates(self, workspace_id: int) -> List[Dict]:
            """List templates in a workspace."""
            return self._request("GET", f"/workspaces/{workspace_id}/extractions/templates")["data"]

        def create_template(self, workspace_id: int, name: str, schema: Dict,
                            description: str = None) -> Dict:
            """Create an extraction template."""
            payload = {
                "workspace_id": workspace_id,
                "name": name,
                "schema_json": schema
            }
            if description:
                payload["description"] = description
            return self._request("POST", "/extractions/templates", json=payload)

        def get_template(self, template_id: str) -> Dict:
            """Get a template by ID."""
            return self._request("GET", f"/extractions/templates/{template_id}")

        # ─────────────────────────────────────────────────────────────
        # File Upload
        # ─────────────────────────────────────────────────────────────

        def upload_file(self, file_path: str) -> str:
            """
            Upload a file and return the storage key.

            Args:
                file_path: Path to the file to upload

            Returns:
                File key to use in batch_create_sessions
            """
            # Get signed upload URL (content_type defaults to 'application/octet-stream')
            upload_data = self._request("POST", "/vapor/signed-storage-url", json={
                "visibility": "private"
            })

            # Upload directly to S3
            with open(file_path, 'rb') as f:
                response = requests.put(
                    upload_data['url'],
                    data=f,
                    headers=upload_data.get('headers', {})
                )
                response.raise_for_status()

            return upload_data['key']

        # ─────────────────────────────────────────────────────────────
        # Batch Operations
        # ─────────────────────────────────────────────────────────────

        def batch_create_sessions(self, template_id: str, file_keys: List[str],
                                   auto_extract: bool = True) -> List[Dict]:
            """
            Create sessions from uploaded files.

            Args:
                template_id: Template UUID
                file_keys: List of file keys from upload_file()
                auto_extract: Start extraction automatically (default: True)
            """
            return self._request(
                "POST",
                f"/extractions/templates/{template_id}/sessions/batch",
                json={
                    "files": file_keys,
                    "settings": {"auto_extract": auto_extract}
                }
            )

        # ─────────────────────────────────────────────────────────────
        # Sessions & Results
        # ─────────────────────────────────────────────────────────────

        def get_session(self, session_id: str) -> Dict:
            """Get session details."""
            return self._request("GET", f"/extractions/sessions/{session_id}")

        def get_results(self, session_id: str) -> List[Dict]:
            """Get extraction results for a session."""
            return self._request("GET", f"/extractions/sessions/{session_id}/results")["data"]

        def get_result(self, result_id: str) -> Dict:
            """Get a specific extraction result with full data."""
            return self._request("GET", f"/extractions/results/{result_id}")

        # ─────────────────────────────────────────────────────────────
        # Workspace Documents
        # ─────────────────────────────────────────────────────────────

        def list_workspace_documents(self, workspace_id: int,
                                     query: str = None, status: str = None,
                                     page: int = None, per_page: int = None) -> Dict:
            """List workspace documents with optional filters."""
            params = {k: v for k, v in [
                ("query", query), ("status", status),
                ("page", page), ("per_page", per_page)
            ] if v is not None}
            return self._request("GET", f"/workspaces/{workspace_id}/documents",
                                params=params)

        def get_workspace_document(self, workspace_id: int,
                                   document_id: str) -> Dict:
            """Get a workspace document by ID."""
            return self._request("GET",
                                 f"/workspaces/{workspace_id}/documents/{document_id}")

        def create_workspace_document(self, workspace_id: int, key: str,
                                      filename: str = None) -> Dict:
            """Create or reuse a workspace document from an upload key."""
            payload = {"key": key}
            if filename:
                payload["filename"] = filename
            return self._request("POST", f"/workspaces/{workspace_id}/documents",
                                 json=payload)

        def import_workspace_document_url(self, workspace_id: int, url: str,
                                          filename: str = None) -> Dict:
            """Import a document from URL into the workspace."""
            payload = {"url": url}
            if filename:
                payload["filename"] = filename
            return self._request("POST",
                                 f"/workspaces/{workspace_id}/documents:importUrl",
                                 json=payload)

        def reparse_workspace_document(self, workspace_id: int, document_id: str,
                                        config_hash: str, force: bool = False) -> Dict:
            """Request parsing for a document with a specific config hash."""
            return self._request("POST",
                                 f"/workspaces/{workspace_id}/documents/{document_id}/reparse",
                                 json={"config_hash": config_hash, "force": force})
    ```

    ### Installation

    ```bash theme={null}
    pip install requests
    ```

    Copy the `RaydocsClient` class into your project or save as `raydocs_client.py`:

    ```python theme={null}
    from raydocs_client import RaydocsClient

    client = RaydocsClient("your_api_token")
    ```
  </Tab>

  <Tab title="PHP">
    ## PHP Client (Guzzle)

    ```php theme={null}
    <?php

    declare(strict_types=1);

    require_once 'vendor/autoload.php';

    use GuzzleHttp\Client;
    use GuzzleHttp\Exception\RequestException;

    class RaydocsClient
    {
        private Client $client;
        private string $baseUrl;

        public function __construct(string $apiKey, string $baseUrl = 'https://api.raydocs.com')
        {
            $this->baseUrl = $baseUrl;
            $this->client = new Client([
                'base_uri' => $baseUrl,
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiKey,
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json',
                ],
            ]);
        }

        private function request(string $method, string $endpoint, ?array $data = null): array
        {
            $options = [];
            if ($data !== null) {
                $options['json'] = $data;
            }

            $response = $this->client->request($method, $endpoint, $options);
            $body = $response->getBody()->getContents();

            return $body ? json_decode($body, true) : [];
        }

        // ─────────────────────────────────────────────────────────────
        // Workspaces
        // ─────────────────────────────────────────────────────────────

        public function listWorkspaces(): array
        {
            return $this->request('GET', '/workspaces')['data'];
        }

        public function createWorkspace(string $name, string $icon = '📊'): array
        {
            return $this->request('POST', '/workspaces/create', [
                'name' => $name,
                'icon' => $icon,
            ]);
        }

        // ─────────────────────────────────────────────────────────────
        // Templates
        // ─────────────────────────────────────────────────────────────

        public function listTemplates(int $workspaceId): array
        {
            return $this->request('GET', "/workspaces/{$workspaceId}/extractions/templates")['data'];
        }

        public function createTemplate(
            int $workspaceId,
            string $name,
            array $schema,
            ?string $description = null
        ): array {
            $payload = [
                'workspace_id' => $workspaceId,
                'name' => $name,
                'schema_json' => $schema,
            ];
            if ($description !== null) {
                $payload['description'] = $description;
            }
            return $this->request('POST', '/extractions/templates', $payload);
        }

        public function getTemplate(string $templateId): array
        {
            return $this->request('GET', "/extractions/templates/{$templateId}");
        }

        // ─────────────────────────────────────────────────────────────
        // File Upload
        // ─────────────────────────────────────────────────────────────

        public function uploadFile(string $filePath): string
        {
            // Get signed upload URL (content_type defaults to 'application/octet-stream')
            $uploadData = $this->request('POST', '/vapor/signed-storage-url', [
                'visibility' => 'private',
            ]);

            // Upload directly to S3 using Guzzle
            $s3Client = new Client();
            $s3Client->put($uploadData['url'], [
                'body' => fopen($filePath, 'r'),
                'headers' => $uploadData['headers'] ?? [],
            ]);

            return $uploadData['key'];
        }

        // ─────────────────────────────────────────────────────────────
        // Batch Operations
        // ─────────────────────────────────────────────────────────────

        public function batchCreateSessions(
            string $templateId,
            array $fileKeys,
            bool $autoExtract = true
        ): array {
            return $this->request(
                'POST',
                "/extractions/templates/{$templateId}/sessions/batch",
                [
                    'files' => $fileKeys,
                    'settings' => ['auto_extract' => $autoExtract],
                ]
            );
        }

        // ─────────────────────────────────────────────────────────────
        // Sessions & Results
        // ─────────────────────────────────────────────────────────────

        public function getSession(string $sessionId): array
        {
            return $this->request('GET', "/extractions/sessions/{$sessionId}");
        }

        public function getResults(string $sessionId): array
        {
            return $this->request('GET', "/extractions/sessions/{$sessionId}/results")['data'];
        }

        public function getResult(string $resultId): array
        {
            return $this->request('GET', "/extractions/results/{$resultId}");
        }

        // ─────────────────────────────────────────────────────────────
        // Workspace Documents
        // ─────────────────────────────────────────────────────────────

        public function listWorkspaceDocuments(
            int $workspaceId,
            array $params = []
        ): array {
            $query = http_build_query(array_filter($params));
            $endpoint = "/workspaces/{$workspaceId}/documents" . ($query ? "?{$query}" : '');
            return $this->request('GET', $endpoint);
        }

        public function getWorkspaceDocument(int $workspaceId, string $documentId): array
        {
            return $this->request('GET', "/workspaces/{$workspaceId}/documents/{$documentId}");
        }

        public function createWorkspaceDocument(
            int $workspaceId,
            string $key,
            ?string $filename = null
        ): array {
            $payload = ['key' => $key];
            if ($filename !== null) {
                $payload['filename'] = $filename;
            }
            return $this->request('POST', "/workspaces/{$workspaceId}/documents", $payload);
        }

        public function importWorkspaceDocumentUrl(
            int $workspaceId,
            string $url,
            ?string $filename = null
        ): array {
            $payload = ['url' => $url];
            if ($filename !== null) {
                $payload['filename'] = $filename;
            }
            return $this->request('POST', "/workspaces/{$workspaceId}/documents:importUrl", $payload);
        }

        public function reparseWorkspaceDocument(
            int $workspaceId,
            string $documentId,
            string $configHash,
            bool $force = false
        ): array {
            return $this->request('POST', "/workspaces/{$workspaceId}/documents/{$documentId}/reparse", [
                'config_hash' => $configHash,
                'force' => $force,
            ]);
        }
    }
    ```

    ### Installation

    ```bash theme={null}
    composer require guzzlehttp/guzzle
    ```

    Save the client as `RaydocsClient.php`:

    ```php theme={null}
    <?php

    require_once 'vendor/autoload.php';
    require_once 'RaydocsClient.php';

    $client = new RaydocsClient('your_api_token');
    ```
  </Tab>
</Tabs>

## Next Steps

<CardGroup cols={2}>
  <Card title="Uploading Documents" icon="upload" href="/api-reference/cookbook/uploading-documents">
    Learn how to upload files for extraction
  </Card>

  <Card title="Quick Start" icon="bolt" href="/api-reference/cookbook/quick-start">
    Extract data from documents in 3 steps
  </Card>

  <Card title="Full Workflow" icon="sitemap" href="/api-reference/cookbook/full-workflow">
    Complete example with template creation
  </Card>

  <Card title="List Workspace Documents" icon="folder" href="/api-reference/documents/list-workspace-documents">
    List and filter workspace-scoped documents
  </Card>
</CardGroup>
