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

# Upload a document

> Three-step flow to upload a private file to S3 and create a document record via the Public API.

Document uploads use three steps: request a presigned URL, upload the file to private storage, then create the document record. Document objects under `documents/` are **not** publicly readable — preview and download always go through signed SquadVault URLs.

## Flow

```mermaid theme={null}
sequenceDiagram
  participant Client
  participant API as SquadVault_API
  participant S3 as Object_storage
  Client->>API: POST /v1/uploads/document-file
  API-->>Client: key, fileRef, uploadUrl
  Client->>S3: PUT file to uploadUrl
  Client->>API: POST /v1/documents with fileKey and fileUrl
  API-->>Client: Document
```

### 1. Request a presigned upload URL

```bash theme={null}
curl -X POST "https://api.squadvault.xyz/v1/uploads/document-file" \
  -H "x-api-key: sv_org_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "organizationId": "YOUR_ORG_ID",
  "contentType": "application/pdf",
  "sizeBytes": 102400
}'
```

Response:

```json theme={null}
{
  "key": "documents/YOUR_ORG_ID/uuid.pdf",
  "fileRef": "s3://your-bucket/documents/YOUR_ORG_ID/uuid.pdf",
  "uploadUrl": "https://...presigned..."
}
```

Map fields when creating the document: `fileKey` ← `key`, `fileUrl` ← `fileRef`. The API overwrites `fileUrl` with a private storage reference.

`sizeBytes` must match the exact byte length of the file you PUT in step 2.

### 2. Upload the file

```bash theme={null}
curl -X PUT "<uploadUrl>" \
  -H "Content-Type: application/pdf" \
  --data-binary @playbook.pdf
```

Use the same `Content-Type` and byte size you requested in step 1.

### 3. Create the document record

```bash theme={null}
curl -X POST "https://api.squadvault.xyz/v1/documents" \
  -H "x-api-key: sv_org_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "organizationId": "YOUR_ORG_ID",
  "name": "Playbook Q3",
  "fileUrl": "<fileRef>",
  "fileKey": "<key>",
  "contentType": "application/pdf",
  "sizeBytes": 102400,
  "tags": ["playbook"]
}'
```

To download or preview later, call `GET /documents/{documentId}/download` or `/preview` — never use `fileUrl` as a public HTTP link.

## Allowed content types

* `application/pdf`
* `application/msword`
* `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
* `application/vnd.ms-excel`
* `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
* `text/plain`
* `image/jpeg`, `image/png`, `image/webp`, `image/gif`

Maximum file size depends on the organization plan (enforced server-side).

## JavaScript example

```javascript theme={null}
const sizeBytes = fileBuffer.byteLength;

const uploadResponse = await fetch(
  "https://api.squadvault.xyz/v1/uploads/document-file",
  {
    method: "POST",
    headers: {
      "x-api-key": process.env.SQUADVAULT_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      organizationId: "YOUR_ORG_ID",
      contentType: "application/pdf",
      sizeBytes,
    }),
  },
);

const { key, fileRef, uploadUrl } = await uploadResponse.json();

await fetch(uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": "application/pdf" },
  body: fileBuffer,
});

const documentResponse = await fetch(
  "https://api.squadvault.xyz/v1/documents",
  {
    method: "POST",
    headers: {
      "x-api-key": process.env.SQUADVAULT_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      organizationId: "YOUR_ORG_ID",
      name: "Playbook Q3",
      fileUrl: fileRef,
      fileKey: key,
      contentType: "application/pdf",
      sizeBytes,
      tags: ["playbook"],
    }),
  },
);

const document = await documentResponse.json();
```

Required permission for both upload URL and document create: `document:create`.
