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

# Manage Company Documents with the Nymble Commerce API

> Upload, organize, and securely share company documents in Nymble Commerce using the Company Docs API with folder hierarchies and signed CDN URLs.

Nymble Commerce's Company Docs feature gives your organization a secure document repository — files are organized in folders, stored in encrypted cloud storage, and served via signed CDN URLs that expire after 1 hour. All data is isolated to your organization; no other tenant can access your documents.

***

## Base URL

All Company Docs endpoints are available under:

```text theme={null}
https://api.achievemomentum.com/api/companydocs
```

Every request must include a valid JWT in the `Authorization` header:

```http theme={null}
Authorization: Bearer {token}
```

The JWT must contain an `OrganizationId` claim. Nymble Commerce uses this claim to enforce organization-level isolation — you only ever see your organization's folders and documents.

***

## Folder management

Folders let you organize documents into a hierarchy. You can nest folders to any depth by setting `parentId` when you create a child folder.

### Create a folder

```http theme={null}
POST https://api.achievemomentum.com/api/companydocs/folders
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "HR Documents",
  "parentId": null
}
```

**Response**

```json theme={null}
{
  "folder": {
    "folderId": "folder_a1b2c3",
    "organizationId": "org_4e9b7f22",
    "name": "HR Documents",
    "parentId": null,
    "path": "/HR Documents",
    "createdAt": "2024-06-01T09:00:00Z",
    "updatedAt": "2024-06-01T09:00:00Z"
  }
}
```

**Name rules:** maximum 255 characters; the `/` and `\` characters are not allowed.

### Build a nested folder structure

Use the `folderId` returned from a parent folder creation as the `parentId` for child folders:

```javascript theme={null}
const token = localStorage.getItem("token");
const headers = {
  Authorization: `Bearer ${token}`,
  "Content-Type": "application/json",
};

// 1. Create root folder
const hrResponse = await fetch(
  "https://api.achievemomentum.com/api/companydocs/folders",
  {
    method: "POST",
    headers,
    body: JSON.stringify({ name: "HR Documents", parentId: null }),
  }
);
const { folder: hrFolder } = await hrResponse.json();

// 2. Create subfolder under HR Documents
const policiesResponse = await fetch(
  "https://api.achievemomentum.com/api/companydocs/folders",
  {
    method: "POST",
    headers,
    body: JSON.stringify({ name: "Policies", parentId: hrFolder.folderId }),
  }
);
const { folder: policiesFolder } = await policiesResponse.json();
// policiesFolder.path → "/HR Documents/Policies"
```

### List all folders

```http theme={null}
GET https://api.achievemomentum.com/api/companydocs/folders
Authorization: Bearer {token}
```

**Response**

```json theme={null}
{
  "folders": [
    {
      "folderId": "folder_a1b2c3",
      "name": "HR Documents",
      "parentId": null,
      "path": "/HR Documents",
      "createdAt": "2024-06-01T09:00:00Z",
      "updatedAt": "2024-06-01T09:00:00Z"
    },
    {
      "folderId": "folder_d4e5f6",
      "name": "Policies",
      "parentId": "folder_a1b2c3",
      "path": "/HR Documents/Policies",
      "createdAt": "2024-06-01T09:05:00Z",
      "updatedAt": "2024-06-01T09:05:00Z"
    }
  ]
}
```

### Delete a folder

```http theme={null}
DELETE https://api.achievemomentum.com/api/companydocs/folders/{folderId}
Authorization: Bearer {token}
```

Returns `204 No Content` on success. You must remove all documents and subfolders from a folder before you can delete it — attempting to delete a non-empty folder returns a `400` error.

***

## Uploading documents

Upload files using a multipart `POST` request. Include the file in the `file` field and optionally specify a target `folderId`.

```http theme={null}
POST https://api.achievemomentum.com/api/companydocs/documents/upload
Authorization: Bearer {token}
Content-Type: multipart/form-data
```

```javascript theme={null}
const formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("folderId", "folder_d4e5f6"); // optional

const response = await fetch(
  "https://api.achievemomentum.com/api/companydocs/documents/upload",
  {
    method: "POST",
    headers: { Authorization: `Bearer ${token}` },
    body: formData,
  }
);
const { document } = await response.json();
```

**Response**

```json theme={null}
{
  "document": {
    "documentId": "doc_9f1e2d3c",
    "organizationId": "org_4e9b7f22",
    "name": "employee-handbook-2024.pdf",
    "fileExtension": ".pdf",
    "mimeType": "application/pdf",
    "size": 2097152,
    "folderId": "folder_d4e5f6",
    "cdnUrl": "https://cdn.achievemomentum.com/docs/org_4e9b7f22/doc_9f1e2d3c?sig=...",
    "path": "/HR Documents/Policies/employee-handbook-2024.pdf",
    "createdAt": "2024-06-01T09:10:00Z",
    "updatedAt": "2024-06-01T09:10:00Z"
  }
}
```

**Constraints:**

* Maximum file size: **50 MB**
* Files are scanned before storage; uploads that fail the virus scan are rejected
* Omit `folderId` (or send `null`) to place the file at the root level

***

## Listing and retrieving documents

List all documents in your organization, or filter to a specific folder:

```http theme={null}
GET https://api.achievemomentum.com/api/companydocs/documents
GET https://api.achievemomentum.com/api/companydocs/documents?folderId=folder_d4e5f6
Authorization: Bearer {token}
```

**Response shape**

```json theme={null}
{
  "documents": [
    {
      "documentId": "doc_9f1e2d3c",
      "name": "employee-handbook-2024.pdf",
      "fileExtension": ".pdf",
      "mimeType": "application/pdf",
      "size": 2097152,
      "folderId": "folder_d4e5f6",
      "cdnUrl": "https://cdn.achievemomentum.com/docs/...",
      "path": "/HR Documents/Policies/employee-handbook-2024.pdf",
      "createdAt": "2024-06-01T09:10:00Z",
      "updatedAt": "2024-06-01T09:10:00Z"
    }
  ]
}
```

Retrieve metadata for a single document:

```http theme={null}
GET https://api.achievemomentum.com/api/companydocs/documents/{documentId}
Authorization: Bearer {token}
```

***

## Downloading documents

To stream a file's binary content directly (for example, to trigger a browser download), use the download endpoint:

```http theme={null}
GET https://api.achievemomentum.com/api/companydocs/documents/{documentId}/download
Authorization: Bearer {token}
```

The response includes the file's binary content with the appropriate `Content-Type` and a `Content-Disposition: attachment; filename="..."` header.

```javascript theme={null}
const response = await fetch(
  `https://api.achievemomentum.com/api/companydocs/documents/${documentId}/download`,
  { headers: { Authorization: `Bearer ${token}` } }
);

const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "employee-handbook-2024.pdf";
a.click();
window.URL.revokeObjectURL(url);
```

***

## Moving documents

Move a single document to a different folder:

```http theme={null}
PUT https://api.achievemomentum.com/api/companydocs/documents/{documentId}/move
Authorization: Bearer {token}
Content-Type: application/json

{
  "targetFolderId": "folder_a1b2c3"
}
```

Set `targetFolderId` to `null` to move the document back to the root level. Returns `204 No Content` on success.

Move multiple documents at once using the bulk endpoint:

```http theme={null}
POST https://api.achievemomentum.com/api/companydocs/documents/bulk-move
Authorization: Bearer {token}
Content-Type: application/json

{
  "documentIds": ["doc_9f1e2d3c", "doc_4a5b6c7d", "doc_8e9f0a1b"],
  "targetFolderId": "folder_a1b2c3"
}
```

***

## CDN URL expiry and refresh

The `cdnUrl` on every document is a **signed URL that expires after 1 hour**. If you cache document metadata and display the URL later, you must refresh it first.

```http theme={null}
POST https://api.achievemomentum.com/api/companydocs/documents/{documentId}/refresh-url
Authorization: Bearer {token}
```

**Response**

```json theme={null}
{
  "cdnUrl": "https://cdn.achievemomentum.com/docs/org_4e9b7f22/doc_9f1e2d3c?sig=newtoken&expires=..."
}
```

```javascript theme={null}
// Example: refresh URL before rendering a download link
async function getFreshUrl(documentId) {
  const response = await fetch(
    `https://api.achievemomentum.com/api/companydocs/documents/${documentId}/refresh-url`,
    {
      method: "POST",
      headers: { Authorization: `Bearer ${token}` },
    }
  );
  const { cdnUrl } = await response.json();
  return cdnUrl;
}
```

<Warning>
  Do not cache `cdnUrl` values for longer than 1 hour. Presenting an expired URL to a user results in an access-denied error from the CDN. Always call `refresh-url` if there is any chance the URL has aged past its expiry window.
</Warning>

***

## Storage statistics

Check your organization's storage usage at any time:

```http theme={null}
GET https://api.achievemomentum.com/api/companydocs/stats/storage
Authorization: Bearer {token}
```

**Response**

```json theme={null}
{
  "stats": {
    "totalFiles": 1247,
    "totalSize": 5368709120,
    "maxSize": 10737418240,
    "usedPercentage": 50.0,
    "availableSize": 5368709120
  }
}
```

| Field            | Description                                 |
| ---------------- | ------------------------------------------- |
| `totalFiles`     | Total number of documents stored            |
| `totalSize`      | Total storage used, in bytes                |
| `maxSize`        | Your organization's storage quota, in bytes |
| `usedPercentage` | Percentage of quota consumed (0–100)        |
| `availableSize`  | Remaining quota, in bytes                   |

***

## Bulk operations

Use bulk endpoints to perform batch actions in a single API call, reducing round-trips and improving performance.

### Bulk delete

Delete up to 100 documents at once:

```http theme={null}
POST https://api.achievemomentum.com/api/companydocs/documents/bulk-delete
Authorization: Bearer {token}
Content-Type: application/json

{
  "documentIds": ["doc_9f1e2d3c", "doc_4a5b6c7d", "doc_8e9f0a1b"]
}
```

```javascript theme={null}
const response = await fetch(
  "https://api.achievemomentum.com/api/companydocs/documents/bulk-delete",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      documentIds: ["doc_9f1e2d3c", "doc_4a5b6c7d", "doc_8e9f0a1b"],
    }),
  }
);
// 204 No Content on success
```

Both `bulk-delete` and `bulk-move` accept a maximum of **100 document IDs** per request. Split larger batches into multiple calls.
