# GetSafeDocs API & MCP Server Documentation

Welcome to the GetSafeDocs REST API and **Model Context Protocol (MCP) Server**! This documentation covers API key authentication, stateless file uploads, secure document sharing, tracking, document requests, mailing lists, malware scan reports, enterprise management, and connecting AI assistants via `/mcp`.

## Base URL

- **API Base URL:** `https://getsafedocs.cloud.run`
- **MCP Server URL:** `https://getsafedocs.cloud.run/mcp` (alias: `https://getsafedocs.cloud.run/api/mcp`)

---

## Authentication

### 1. API Key Authentication (Recommended)

You can authenticate any API request using a personal **API Key** without needing a password, browser cookies, or interactive MFA prompts. Works with all account types (including Google Sign-In, Passkey, and MFA-protected accounts).

#### How to Get Your API Key
1. Sign in to GetSafeDocs and go to your **[Profile](https://getsafedocs.cloud.run/profile)**.
2. Under **Security Settings → Developer API Keys**, enter a key name (e.g., `CLI Script` or `CI/CD Pipeline`), choose an optional expiration (`Never expires`, `30 days`, `90 days`, `1 year`), and click **Generate API Key**.
3. Copy your `gsd_...` key immediately (for security, only the SHA-256 hash is stored and the full key is shown only once).

#### Passing Your API Key in Requests
Include your API key in the `X-API-Key` header or the `Authorization: Bearer` header:

```bash
# Option A: X-API-Key header
curl -H "X-API-Key: gsd_your_api_key_here" \
  https://getsafedocs.cloud.run/api/whoami

# Option B: Authorization Bearer header
curl -H "Authorization: Bearer gsd_your_api_key_here" \
  https://getsafedocs.cloud.run/api/whoami
```

---

### 2. Verify Authentication (`/api/whoami`)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/whoami` (alias: `GET /api/me`)
- **Authentication:** Required (API Key or Session)
- **Response:**
```json
{
  "success": true,
  "auth_method": "api_key",
  "api_key_id": 4,
  "account": {
    "id": 123,
    "email": "user@example.com",
    "access_level": "premium",
    "is_company_admin": false,
    "company_id": null,
    "mfa_enabled": true
  },
  "limits": {
    "messages_per_month": 300,
    "max_attachment_size_mb": 128,
    "max_attachments": 20,
    "max_recipients": 30,
    "max_received_size_mb": 3000
  },
  "usage": {
    "message_count": 12,
    "sent_kb": 15420,
    "received_kb": 4100
  }
}
```

---

### 3. Managing API Keys via API

#### List Active API Keys
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/api_keys`
- **Authentication:** Required
- **Response:**
```json
{
  "success": true,
  "count": 1,
  "api_keys": [
    {
      "id": 4,
      "name": "CI/CD Pipeline",
      "key_prefix": "gsd_8f9a1b2c...4e5f",
      "scopes": "full",
      "last_used_at": "2026-11-09 17:15:00",
      "last_used_ip": "203.0.113.10",
      "expires_at": null,
      "is_active": 1,
      "is_expired": 0,
      "created_at": "2026-11-09 16:00:00"
    }
  ]
}
```

#### Create a New API Key
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/api_keys` (alias: `POST /api/create_api_key`)
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "name": "Production Server",
  "expires_days": 90
}
```
- **Response:**
```json
{
  "success": true,
  "api_key": "gsd_8f9a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6",
  "key": {
    "id": 5,
    "name": "Production Server",
    "key_prefix": "gsd_8f9a1b2c...f5a6",
    "scopes": "full",
    "expires_at": "2027-02-07 17:20:00",
    "created_at": "2026-11-09 17:20:00"
  },
  "message": "API key generated. Copy this key now — for security, the full key will not be shown again."
}
```

#### Revoke an API Key
- **Endpoint:** `DELETE https://getsafedocs.cloud.run/api/api_keys` or `POST https://getsafedocs.cloud.run/api/revoke_api_key`
- **Authentication:** Required
- **Body (JSON):** `{ "key_id": 5 }`
- **Response:** `{ "success": true, "message": "API key revoked", "key_id": 5 }`

---

### 4. Legacy Session Authentication (Optional)

If you are building a session-based client instead of using API keys, session endpoints remain supported:

- **Register:** `POST https://getsafedocs.cloud.run/api/register` (`{ "email": "...", "password": "..." }`)
- **Login:** `POST https://getsafedocs.cloud.run/api/login` (`{ "email": "...", "password": "..." }`)
- **Logout:** `POST https://getsafedocs.cloud.run/api/logout`
- **Refresh Session:** `POST https://getsafedocs.cloud.run/api/refresh_token`

---

## File Uploads & Stateless Message Sending

All file uploads use a 3-step direct-to-GCS signed URL workflow with automatic MIME inspection and QuickSand malware analysis. When using an API key, uploaded files are tracked statelessly by their `uuid` so no cookies or sessions are needed.

### Step 1: Request a Signed Upload URL
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/get_signed_url` (also available at `/get_signed_url`)
- **Authentication:** Required (`X-API-Key` or `Authorization: Bearer`)
- **Body (JSON):**
```json
{
  "filename": "contract.pdf",
  "contentType": "application/pdf",
  "fileSize": 1024000
}
```
- **Response:**
```json
{
  "success": true,
  "signedUrl": "https://storage.googleapis.com/bucket/uploads/...",
  "tempObject": "uploads/12345678-1234-1234-1234-123456789012_contract.pdf",
  "tempBucketPath": "uploads/12345678-1234-1234-1234-123456789012_contract.pdf",
  "uuid": "12345678-1234-1234-1234-123456789012",
  "kmsKeyName": "projects/.../cryptoKeys/..."
}
```

### Step 2: Upload Raw File Bytes to GCS
- **Method:** `PUT` to the `signedUrl` returned in Step 1
- **Headers:**
  - `Content-Type`: Match the `contentType` from Step 1 (Note: CMEK encryption key is already embedded as a signed query parameter on `signedUrl`; do not pass an extra `x-goog-encryption-kms-key-name` header)
- **Body:** Raw binary file content

### Step 3: Process & Scan the Uploaded File
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/upload_file` (alias: `POST /api/process_cloud_upload`)
- **Authentication:** Required (`X-API-Key` or `Authorization: Bearer`)
- **Body (JSON):**
```json
{
  "tempObject": "uploads/12345678-1234-1234-1234-123456789012_contract.pdf",
  "filename": "contract.pdf",
  "contentType": "application/pdf",
  "uuid": "12345678-1234-1234-1234-123456789012"
}
```
- **Response:**
```json
{
  "success": true,
  "file_id": "12345678-1234-1234-1234-123456789012",
  "uuid": "12345678-1234-1234-1234-123456789012",
  "file": {
    "uuid": "12345678-1234-1234-1234-123456789012",
    "access_uuid": "9f8e7d6c5b4a...",
    "scan_id": 42,
    "name": "contract.pdf",
    "size": 1024000,
    "type": "application/pdf",
    "scan_rating": 0,
    "scan_risk": "Clean"
  },
  "scan_result": {
    "rating": 0,
    "risk": "Clean",
    "uuid": "12345678-1234-1234-1234-123456789012",
    "access_uuid": "9f8e7d6c5b4a...",
    "scan_id": 42
  }
}
```

### Delete a Temporary Uploaded File (Before Sending)
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/delete_file`
- **Authentication:** Required
- **Body (JSON):** `{ "uuid": "12345678-1234-1234-1234-123456789012" }`

---

## Messages & Document Sharing

### Send a Secure Message
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/send_message`
- **Authentication:** Required
- **Content-Type:** `application/json` or `application/x-www-form-urlencoded`
- **Fields:**
  - `recipients` (array of strings or comma-separated string) — Recipient email addresses. Omit and pass `"generate_token_only": true` to generate a manual share token without emailing.
  - `mailing_list_id` (integer, optional) — Send to all recipients in a saved external mailing list.
  - `team_ids` (array of integers, optional) — Expand internal Enterprise Teams into recipients (Enterprise tier).
  - `subject` (string, optional, default `"Document Delivery"`) — Message subject.
  - `body` or `message` (string, optional if files are attached) — Message text.
  - `file_uuids` or `uploaded_file_ids` (array of UUID strings or comma-separated string, optional) — File UUIDs from `/api/upload_file`.
  - `repo_file_ids` (array of integers, optional) — Attach existing Enterprise Document Repository files (`repo_file_id`) directly without re-uploading (one-click resend).
  - `save_to_repository` (boolean, optional, default `false`) — Archive newly uploaded attachments into the Enterprise Document Repository upon sending.
  - `classification` (string, optional, default `"None"`) — Enterprise classification marking (`None`, `Confidential`, `NTK`, `Restricted`, or custom company markings).
  - `expiry_days` (integer, optional, default `30`, max `365`) — Days until the message expires.
  - `notify_on_activity` (boolean, optional, default `true`) — Email sender when recipients view/download files.
  - `preview_only` (boolean, optional, default `false`) — Restrict recipients to safe sandbox viewing only (disable raw downloads).
  - `send_email` (boolean, optional, default `true`) — Automatically email share links to recipients.
  - `generate_token_only` (boolean, optional, default `false`) — Generate a shareable `access_token` and `access_url` without specifying recipient emails.
- **Example Request (JSON):**
```json
{
  "recipients": ["client@example.com", "legal@example.com"],
  "subject": "Signed Q4 Agreement",
  "body": "Please find the signed agreement attached.",
  "file_uuids": ["12345678-1234-1234-1234-123456789012"],
  "expiry_days": 30,
  "notify_on_activity": true,
  "preview_only": false
}
```
- **Response:**
```json
{
  "success": true,
  "message_id": 123,
  "subject": "Signed Q4 Agreement",
  "created_at": "2026-11-09 17:30:00",
  "expiry_date": "2026-12-09 17:30:00",
  "preview_only": false,
  "recipients": [
    {
      "recipient_id": 456,
      "email": "client@example.com",
      "access_token": "k9m2p4x7",
      "access_url": "https://getsafedocs.cloud.run/access_document?token=k9m2p4x7",
      "email_sent": true
    }
  ],
  "files": [
    {
      "file_id": 789,
      "file_name": "contract.pdf",
      "mime_type": "application/pdf",
      "uuid": "12345678-1234-1234-1234-123456789012",
      "access_uuid": "9f8e7d6c5b4a...",
      "scan_rating": 0,
      "scan_risk": "Clean",
      "preview_only": false
    }
  ]
}
```

### List Sent Messages (Outbox)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/list_messages?limit=20&offset=0&search=`
- **Authentication:** Required
- **Response:** Returns `messages` (each including `recipients`, `files`, `file_count`, `unread_replies_count`) and `pagination`.

### List Received Messages (Inbox)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/list_received_messages?limit=20&offset=0&archived=0&unread_only=0&search=`
- **Authentication:** Required
- **Query Parameters:**
  - `archived`: `0` (active inbox, default), `1` (archived), or `all`
  - `unread_only`: `1` or `true` to filter unread messages
  - `search`: Filter by subject, body, or sender email

### View Message Details
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/message_details?message_id=123`
- **Authentication:** Required (sender or recipient)
- **Response:** Returns `message`, `files`, `recipients` (with `access_token` and `access_url` when requested by sender), and `replies`.

### Message Activity & Tracking
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/message_tracking?message_id=123`
- **Authentication:** Required (sender only)
- **Response:** Returns `activities` / `tracking` (views, downloads, timestamps, IPs, user agents) and `notifications`.

### Resend Recipient Notification Emails
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/resend_notification`
- **Authentication:** Required (sender only)
- **Body (JSON):**
```json
{
  "message_id": 123,
  "emails": ["client@example.com"]
}
```

### Archive / Unarchive Message
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/archive_message` (or `POST /api/unarchive_message`)
- **Authentication:** Required
- **Body (JSON):** `{ "message_id": 123, "archive": true }` (set `"archive": false` to unarchive)

### Delete Sent Message
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/delete_message`
- **Authentication:** Required (sender only)
- **Body (JSON):** `{ "message_id": 123 }`
  - *Note:* If the sender is under a **Visible Legal Hold**, deletion returns HTTP `403`. If under a **Silent (Stealth) Legal Hold**, the message is hidden from the sender's Outbox (`sender_deleted = 1`) while preserved in the Enterprise eDiscovery Vault.

### Promote Attachment to Enterprise Repository
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/save_file_to_repo`
- **Authentication:** Required (Enterprise sender or Company Admin)
- **Body (JSON):** `{ "file_id": 789, "message_id": 123, "folder_name": "Contracts", "visibility": "private" }`

### Toggle Message Legal Hold
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/toggle_message_legal_hold`
- **Authentication:** Required (Enterprise Company Admin or System Admin)
- **Body (JSON):** `{ "message_id": 123, "legal_hold": true }`

### Access Shared Message via Recipient Token (No Account Required)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/access_document?token=k9m2p4x7`
- **Authentication:** Token-based (`token` query parameter)
- **Response:** Returns `message`, `files`, and `replies`.

---

## Files, Downloads, ZIP Archives & Scan Reports

### Get File Metadata & Access Statistics
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/get_file_metadata?file_id=789` (or `?uuid=...`)
- **Authentication:** Required

### Download Single Attachment (Signed URL)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/download_attachment?file_id=789` (or `?uuid=...&token=...`)
- **Authentication:** API Key, Session, or Recipient `token`
- **Response:**
```json
{
  "success": true,
  "signed_url": "https://storage.googleapis.com/...",
  "signedUrl": "https://storage.googleapis.com/...",
  "filename": "contract.pdf",
  "expires_in": 900
}
```

### Download All Message Files as ZIP (Signed URL)
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/download_zip?message_id=123` (or `?token=k9m2p4x7`)
- **Authentication:** API Key, Session, or Recipient `token`
- **Response:**
```json
{
  "success": true,
  "signed_url": "https://storage.googleapis.com/...",
  "signedUrl": "https://storage.googleapis.com/...",
  "file_count": 3
}
```

### Get Malware Scan Report
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/scan_report?uuid=12345678-...` (or `?file_id=789` or `?scan_id=42`)
- **Response:** Returns cryptographic hashes (`md5`, `sha1`, `sha256`), `risk_score`, `risk_rating`, and detailed QuickSand static analysis results.

---

## Replies

### Add Reply
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/add_reply`
- **Authentication:** API Key, Session, or Recipient `token`
- **Body (JSON):** `{ "message_id": 123, "reply_text": "Thank you, received!", "token": "optional_recipient_token" }`

### List Replies
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/list_replies?message_id=123`
- **Authentication:** API Key, Session, or `?token=recipient_token`

### Mark Reply as Read
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/mark_reply_read`
- **Authentication:** Required
- **Body (JSON):** `{ "reply_id": 42 }`

---

## Document Requests

Document requests allow external users to upload files to you using a secure `access_token` without needing a GetSafeDocs account.

### Create Document Request
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/create_document_request`
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "request_name": "Tax Documents 2026",
  "description": "Please upload your signed tax forms and receipts",
  "allow_multiple_submissions": true,
  "max_file_size_mb": 50,
  "max_files_per_submission": 10,
  "allowed_file_types": "pdf,doc,docx,xlsx",
  "require_email": true,
  "require_subject": false,
  "require_message": true,
  "expiry_days": 30,
  "notify_emails": ["client@example.com"]
}
```

### List Document Requests
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/list_document_requests?status=all&limit=50&offset=0`
- **Authentication:** Required
- **Query Parameters:** `status` (`active`, `closed`, `expired`, `all`), `limit`, `offset`

### Get Document Request & Submissions
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/get_document_request?request_id=123` (or `?request_token=abc123def456`)
- **Authentication:** Required
- **Response:** Returns the `request` object and all `submissions` with their attached `files`.

### Update Document Request
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/update_document_request`
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "request_id": 123,
  "request_name": "Updated Tax Documents",
  "description": "Updated instructions",
  "is_active": true,
  "max_file_size_mb": 100,
  "expiry_days": 60
}
```

### Delete Document Request
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/delete_document_request`
- **Authentication:** Required
- **Body (JSON):** `{ "request_id": 123 }`

### Get Single Submission Details
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/get_submission_details?submission_id=45`
- **Authentication:** Required

### Public Upload to a Document Request (Token-Based)
1. **Get Signed Upload URL:** `POST https://getsafedocs.cloud.run/api/get_document_request_signed_url`
   - Body: `{ "filename": "form.pdf", "contentType": "application/pdf", "fileSize": 512000, "access_token": "abc123def456" }`
2. **PUT File to GCS:** `PUT` raw bytes to `signedUrl`
3. **Complete Submission:** `POST https://getsafedocs.cloud.run/api/upload_document_request_file`
   - Body: `{ "tempObject": "...", "uuid": "...", "filename": "form.pdf", "contentType": "application/pdf", "access_token": "abc123def456", "submitter_email": "client@example.com", "submitter_name": "Jane Doe", "notes": "Attached!" }`

---

## Mailing Lists

### List Mailing Lists
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/mailing_lists`
- **Authentication:** Required
- **Response:** Returns all mailing lists owned by the account, including `recipients` and `recipient_count`.

### Create Mailing List
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/mailing_lists`
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "name": "Board of Directors",
  "description": "Monthly board packet distribution list",
  "emails": ["director1@example.com", "director2@example.com"]
}
```

### Update Mailing List
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/mailing_lists/update`
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "list_id": 10,
  "name": "Board of Directors",
  "add_emails": ["newdirector@example.com"],
  "remove_emails": ["olddirector@example.com"]
}
```
*(You can also pass `"emails": [...]` to replace the entire recipient list.)*

### Delete Mailing List
- **Endpoint:** `POST https://getsafedocs.cloud.run/api/mailing_lists/delete` (or `DELETE /api/mailing_lists`)
- **Authentication:** Required
- **Body (JSON):** `{ "list_id": 10 }`

### Get Mailing List Recipients
- **Endpoint:** `GET https://getsafedocs.cloud.run/api/mailing_lists/recipients?list_id=10`
- **Authentication:** Required

---

## User Profile, Usage & File Types

- **Get Profile:** `GET https://getsafedocs.cloud.run/api/get_profile`
- **Update Profile:** `POST https://getsafedocs.cloud.run/api/update_profile` (`{ "email_marketing_opt_in": false }`)
- **Get Usage Statistics:** `GET https://getsafedocs.cloud.run/api/get_usage_stats`
- **Get Allowed File Types & Tier Limits:** `GET https://getsafedocs.cloud.run/api/file_types`
- **System Status:** `GET https://getsafedocs.cloud.run/api/get_system_status`

---

## Bulk Operations

- **Endpoint:** `POST https://getsafedocs.cloud.run/api/bulk_operations`
- **Authentication:** Required
- **Body (JSON):**
```json
{
  "operation": "archive_messages",
  "targets": [123, 456, 789]
}
```
- **Supported Operations:** `archive_messages`, `unarchive_messages`, `mark_messages_read`, `delete_messages`, `delete_document_requests`

---

## Enterprise Repository, Teams, Legal Hold & Admin Endpoints

### Enterprise Document Repository (`/api/repository/*`)
- **List & Search Repository Files:** `GET https://getsafedocs.cloud.run/api/repository/files?folder=&classification=&scope=all&q=`
- **Upload or Promote Files to Repository:** `POST https://getsafedocs.cloud.run/api/repository/upload`
  - Body: `{ "file_uuids": ["..."], "folder_name": "Contracts", "classification": "Confidential", "visibility": "shared", "share_team_ids": [1], "can_view": true, "can_download": true, "can_resend": true, "can_delete": false }` (or pass `"file_id": 789` to promote an existing message attachment)
- **Update Repository File Metadata:** `POST https://getsafedocs.cloud.run/api/repository/update`
  - Body: `{ "repo_file_id": 12, "folder_name": "Legal", "description": "Master MSA", "visibility": "company", "classification": "Confidential" }`
- **Grant Granular Share (Team or User):** `POST https://getsafedocs.cloud.run/api/repository/share`
  - Body: `{ "repo_file_id": 12, "target_type": "team", "target_team_id": 1, "can_view": true, "can_download": true, "can_resend": true, "can_delete": false }`
- **Revoke Share:** `POST https://getsafedocs.cloud.run/api/repository/unshare` (`{ "share_id": 5 }`)
- **Toggle Repository File Legal Hold:** `POST https://getsafedocs.cloud.run/api/repository/legal_hold` (`{ "repo_file_id": 12, "legal_hold": true }`)
- **Download Repository File (Signed URL):** `GET https://getsafedocs.cloud.run/api/repository/download?repo_file_id=12`
- **Delete Repository File:** `POST https://getsafedocs.cloud.run/api/repository/delete` (`{ "repo_file_id": 12, "purge_reason": "Superseded" }`)

### Enterprise Company Admin (`/api/enterprise/*`)
- **Overview (Members, Seats, Teams, Held Messages / eDiscovery Vault, Invites, Settings):** `GET https://getsafedocs.cloud.run/api/enterprise/overview`
- **Create / Rename Company:** `POST /api/enterprise/create_company` / `POST /api/enterprise/rename_company` (`{ "company_name": "Acme Corp" }`)
- **Update Security & Repository Governance Settings:** `POST https://getsafedocs.cloud.run/api/enterprise/update_settings`
  - Body: `{ "classification_options": ["None", "Confidential", "NTK", "Restricted"], "disable_remote_preview": true, "auto_archive_to_repo": true, "repo_retention_days": 365 }`
- **Invite Member:** `POST https://getsafedocs.cloud.run/api/enterprise/invite` (`{ "email": "teammate@company.com" }`)
- **Resend / Cancel Invite:** `POST /api/enterprise/resend_invite` / `POST /api/enterprise/cancel_invite` (`{ "invite_id": 10 }`)
- **Accept / Decline Invite (Invited User):** `POST https://getsafedocs.cloud.run/api/enterprise/accept_invite` (`{ "invite_token": "...", "decision": "accept" }`)
- **Set Member Role / Tier:** `POST https://getsafedocs.cloud.run/api/enterprise/set_member_role` (`{ "account_id": 456, "is_company_admin": true }`)
- **Remove Member:** `POST https://getsafedocs.cloud.run/api/enterprise/remove_member` (`{ "account_id": 456 }`)
- **Set User Legal Hold (Visible, Silent/Stealth, or Off):** `POST https://getsafedocs.cloud.run/api/enterprise/set_legal_hold`
  - Body: `{ "account_id": 456, "legal_hold_mode": "silent", "legal_hold_reason": "Case #2026-09" }`
- **Manage Internal Enterprise Teams:** `GET / POST https://getsafedocs.cloud.run/api/enterprise/teams`
  - Actions (`action`): `create_team`, `delete_team`, `add_team_member`, `remove_team_member`

### System Admin (`/api/admin/*`)
- **List Users:** `GET https://getsafedocs.cloud.run/api/admin/list_users?limit=50&offset=0`
- **Update User:** `POST https://getsafedocs.cloud.run/api/admin/update_user`
- **Security Logs:** `GET https://getsafedocs.cloud.run/api/admin/get_security_logs`
- **Malware Detections:** `GET https://getsafedocs.cloud.run/api/admin/get_malware_detections`
- **Rescan File:** `POST https://getsafedocs.cloud.run/api/rescan_file` (`{ "uuid": "..." }`)

---

## Code Examples

### Complete Python Example (Upload & Send with API Key)

```python
import os
import requests

API_BASE = "https://getsafedocs.cloud.run"
API_KEY = os.environ["GETSAFEDOCS_API_KEY"]  # e.g. "gsd_..."
HEADERS = {"X-API-Key": API_KEY}

file_path = "contract.pdf"
file_size = os.path.getsize(file_path)

# 1. Get signed upload URL
sign_resp = requests.post(
    f"{API_BASE}/api/get_signed_url",
    headers=HEADERS,
    json={
        "filename": "contract.pdf",
        "contentType": "application/pdf",
        "fileSize": file_size,
    },
).json()

# 2. Upload file bytes to GCS (KMS key is pre-signed in the URL query string)
put_headers = {"Content-Type": "application/pdf"}

with open(file_path, "rb") as f:
    requests.put(sign_resp["signedUrl"], data=f, headers=put_headers).raise_for_status()

# 3. Process & malware-scan the uploaded file
upload_resp = requests.post(
    f"{API_BASE}/api/upload_file",
    headers=HEADERS,
    json={
        "tempObject": sign_resp["tempObject"],
        "uuid": sign_resp["uuid"],
        "filename": "contract.pdf",
        "contentType": "application/pdf",
    },
).json()

# 4. Send the secure message
send_resp = requests.post(
    f"{API_BASE}/api/send_message",
    headers=HEADERS,
    json={
        "recipients": ["recipient@example.com"],
        "subject": "Quarterly Contract",
        "body": "Please review the attached contract.",
        "file_uuids": [upload_resp["uuid"]],
        "expiry_days": 30,
    },
).json()

print("Sent message ID:", send_resp["message_id"])
print("Recipient access link:", send_resp["recipients"][0]["access_url"])
```

### Complete JavaScript / Node.js Example (with API Key)

```javascript
const API_BASE = 'https://getsafedocs.cloud.run';
const API_KEY = process.env.GETSAFEDOCS_API_KEY; // "gsd_..."
const authHeaders = {
  'Content-Type': 'application/json',
  'X-API-Key': API_KEY
};

// Step 1: Get signed URL
const signRes = await fetch(`${API_BASE}/api/get_signed_url`, {
  method: 'POST',
  headers: authHeaders,
  body: JSON.stringify({
    filename: file.name,
    contentType: file.type || 'application/pdf',
    fileSize: file.size
  })
});
const { signedUrl, tempObject, uuid } = await signRes.json();

// Step 2: Upload file directly to GCS
const putHeaders = { 'Content-Type': file.type || 'application/pdf' };
await fetch(signedUrl, { method: 'PUT', headers: putHeaders, body: file });

// Step 3: Process & scan upload
const procRes = await fetch(`${API_BASE}/api/upload_file`, {
  method: 'POST',
  headers: authHeaders,
  body: JSON.stringify({
    tempObject,
    uuid,
    filename: file.name,
    contentType: file.type || 'application/pdf'
  })
});
const { file_id } = await procRes.json();

// Step 4: Send message with attached file UUID
const sendRes = await fetch(`${API_BASE}/api/send_message`, {
  method: 'POST',
  headers: authHeaders,
  body: JSON.stringify({
    recipients: ['user@example.com'],
    subject: 'Secure Document Delivery',
    body: 'Here is the requested document.',
    file_uuids: [file_id]
  })
});
const result = await sendRes.json();
console.log('Message sent:', result);
```

---

## Model Context Protocol (MCP) Server (`/mcp`)

GetSafeDocs provides a built-in **Model Context Protocol (MCP) Server** at `https://getsafedocs.cloud.run/mcp` (and `/api/mcp`) implementing JSON-RPC 2.0 over Streamable HTTP (`2025-03-26` & `2024-11-05`). You can connect AI assistants (Claude Desktop, Cursor, Antigravity, Windsurf, or custom MCP agents) directly using your personal `gsd_...` API Key.

### 1. Connecting via Remote Streamable HTTP (Recommended — Zero Install)

Add the following to your AI client's MCP configuration file (e.g., `mcp_config.json` or `.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "getsafedocs": {
      "url": "https://getsafedocs.cloud.run/mcp",
      "headers": {
        "X-API-Key": "gsd_your_api_key_here"
      }
    }
  }
}
```

### 2. Connecting via Local STDIO Bridge (For Local Disk File Uploads)

If you want your desktop AI assistant to upload files directly from local disk paths (e.g., `"Send ~/Documents/contract.pdf to alice@example.com"`), download the zero-dependency Python STDIO script (`curl -sSL https://getsafedocs.cloud.run/mcp/getsafedocs_mcp.py -o ~/getsafedocs_mcp.py`):

```json
{
  "mcpServers": {
    "getsafedocs": {
      "command": "python3",
      "args": ["/path/to/mcp/getsafedocs_mcp.py"],
      "env": {
        "GETSAFEDOCS_API_KEY": "gsd_your_api_key_here"
      }
    }
  }
}
```

### 3. Available MCP Tools (`tools/list` & `tools/call`)

| Tool Name | Description |
|-----------|-------------|
| `whoami` | Get authenticated account profile, tier (`free`, `premium`, `enterprise`), quota limits, and monthly usage. |
| `get_supported_file_types` | List allowed and forbidden file extensions and max file size limits per tier. |
| `upload_inline_file` | Upload a file from `content_text` or `content_base64` to Toronto staging (`northamerica-northeast2`) and run QuickSand malware analysis. |
| `get_upload_signed_url` | Generate a GCS V4 Signed `PUT` URL (`signedUrl`, `tempObject`, `uuid`) for uploading large binary files. |
| `process_and_scan_upload` | Verify a file uploaded via `get_upload_signed_url` and run QuickSand malware analysis. |
| `send_secure_message` | Send a secure message with `file_uuids`, `repo_file_ids` (one-click resend from Enterprise Repository), or `inline_files` (uploads, scans, and sends in one call), `recipients`, `mailing_list_id`, `team_ids` (internal Enterprise Teams), `save_to_repository`, `classification`, `expiry_days`, `preview_only`, and `send_emails`. |
| `list_messages` | List messages in `outbox` (sent) or `inbox` (received) with pagination. |
| `get_message_details` | Get message details, attached files, malware scan ratings, recipient delivery/read status, activity audit logs, and replies. |
| `reply_to_message` | Send a reply to a message thread using `message_id` or `token`. |
| `manage_message` | Perform message & recipient lifecycle actions: `extend_expiry`, `toggle_notify`, `set_classification`, `resend_notification`, `add_recipients` (supports `team_ids` & `mailing_list_id`), `remove_recipient`, `add_files` (supports `repo_file_ids` & `save_to_repository`), `remove_file`, `save_file_to_repo`, `toggle_legal_hold`, `toggle_preview_only`, `mark_read`, `mark_reply_read`, `archive`, `unarchive`, or `delete`. |
| `download_attachment` | Generate a short-lived GCS V4 signed download URL for a file (`file_id` or `uuid`) or a ZIP of all message files (`as_zip: true`), or save directly to local disk (`save_to_path` in STDIO bridge). |
| `get_scan_report` | Inspect QuickSand malware analysis findings and cryptographic hashes (`MD5`, `SHA-1`, `SHA-256`, `SHA-512`) for a file `uuid`. |
| `access_shared_document` | Access a shared document package using a `token` or `message_id`. |
| `list_document_requests` | List all Document Request intake portals created by the user (optional `status` filter). |
| `get_document_request` | Get details and client file submissions for a specific Document Request (`request_id` or `request_token`). |
| `submit_to_document_request` | Upload and submit files (`inline_files` or `local_file_paths` in STDIO bridge) to a Document Request portal using its `request_token`. |
| `create_document_request` | Create a token-protected Document Request intake portal (`request_name`, `expiry_days`, `max_file_size_mb`, `allowed_extensions`, `notify_emails`). |
| `update_document_request` | Update, `activate`, `deactivate`, email (`notify_emails`), or `delete` a Document Request portal. |
| `manage_mailing_lists` | List, inspect (`get`), `create`, `update`, or `delete` external recipient Mailing Lists. |
| `enterprise_admin` | Enterprise Company Admins: `overview` (seat usage, members, internal teams, held_messages eDiscovery vault, invites, settings), `create_company`, `rename_company`, `update_settings` (`classification_options`, `disable_remote_preview`, `auto_archive_to_repo`, `repo_retention_days`), `invite`, `resend_invite`, `cancel_invite`, `accept_invite`, `set_member_role`, `remove_member`, `set_user_legal_hold` (`visible`, `silent`, `off`), `toggle_message_legal_hold`, `list_teams`, `create_team`, `delete_team`, `add_team_member`, and `remove_team_member`. |
| `enterprise_repository` | Enterprise Document Repository: `list` (filter by folder, classification, scope, search), `upload` (via `file_uuids`, `inline_files`, `local_file_paths`, or promote `file_id`), `update`, `share` (grant `can_view`, `can_download`, `can_resend`, `can_delete` to a `team` or `user`), `unshare`, `toggle_legal_hold`, `download` (supports `save_to_path` in STDIO bridge), and `delete`. |

### 4. Available MCP Resources (`resources/list` & `resources/read`)

- `getsafedocs://guide/quickstart` — Complete MCP & API integration guide and workflow reference
- `getsafedocs://account/profile` — Current account profile, tier limits, and monthly quota usage
- `getsafedocs://config/file-types` — Supported & forbidden file extensions per tier
- `getsafedocs://messages/outbox` — Recent Outbox (sent) messages
- `getsafedocs://messages/inbox` — Recent Inbox (received) messages
- `getsafedocs://document-requests` — Active Document Request intake portals
- `getsafedocs://mailing-lists` — Saved recipient mailing lists
- `getsafedocs://repository` — Enterprise Document Repository files, folders, classifications, and permissions
- **Resource Templates (`resources/templates/list`):**
  - `getsafedocs://messages/{message_id}`
  - `getsafedocs://scan-report/{uuid}`
  - `getsafedocs://document-requests/{request_id}`

### 5. Example MCP JSON-RPC Call (`tools/call`)

```bash
curl -X POST https://getsafedocs.cloud.run/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: gsd_your_api_key_here" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "send_secure_message",
      "arguments": {
        "subject": "Security Audit Summary",
        "body": "Attached is the Q4 security summary.",
        "recipients": ["client@example.com"],
        "preview_only": true,
        "inline_files": [
          {
            "filename": "q4_summary.md",
            "content_text": "# Q4 Security Summary\n\nAll systems passed 99/100 OWASP assessment."
          }
        ]
      }
    }
  }'
```

---

## Related Documentation

- **Quick Start Guide:** [View Quick Start Guide](markdown_viewer.html?file=quick_start_guide)
- **Complete User Guide:** [View User Guide](markdown_viewer.html?file=user_guide)
- **Enterprise Admin Guide:** [View Enterprise Admin Guide](markdown_viewer.html?file=enterprise_admin_guide)
- **Supported File Types (Web / JSON / Markdown):** [https://getsafedocs.cloud.run/file_types](https://getsafedocs.cloud.run/file_types) | [https://getsafedocs.cloud.run/file_types?format=json](https://getsafedocs.cloud.run/file_types?format=json)
- **Security Documentation:** [View Security Documentation](security-documentation.html)
- **Contact Support:** [https://getsafedocs.cloud.run/contact](https://getsafedocs.cloud.run/contact)

