Skip to main content
POST
/
v1
/
files
/
presigned-urls
Upload Files
const options = {
  method: 'POST',
  headers: {
    Authorization: '<authorization>',
    'x-organization-id': '<x-organization-id>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: '<string>',
    extension: '<string>',
    mime_type: '<string>',
    size_kb: 123,
    folder_id: '<string>',
    file_ids: ['<string>'],
    status: '<string>',
    project_id: '<string>'
  })
};

fetch('https://api.dcycle.io/v1/files/presigned-urls', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
{
  "id": "<string>",
  "status": "<string>",
  "url": "<string>",
  "presigned_url": "<string>"
}

Upload Files

Use this 3-step handshake to upload files from browsers, scripts, CI jobs, or the Dcycle CLI without streaming file bytes through the API server.
1

Create presigned upload URLs

Call POST /v1/files/presigned-urls. The backend creates pending file rows and returns one presigned S3 URL per file.
2

Upload file bytes to S3

PUT the raw file bytes to each presigned URL using the same Content-Type you sent in step 1.
3

Confirm the upload

Call PATCH /v1/files/batch-update with status=uploaded. This marks the file as uploaded and emits CLASSIFY_DOCUMENT.

Step 1: Create Presigned URLs

Headers

Authorization
string
required
Bearer token or API token for the authenticated user.
x-organization-id
string
required
Organization UUID that owns the uploaded files.
x-partner
string
Partner header. Use dcycle unless you were given a different integration value.

Body

Send an array so one request can prepare multiple uploads.
name
string
required
File name without the extension.
extension
string
required
File extension such as pdf, csv, xlsx, jpg, or png.
mime_type
string
required
MIME type for the uploaded file.
size_kb
integer
required
File size in kilobytes.
folder_id
string
Optional folder UUID. Omit for root-level uploads.

Example

curl -X POST "https://api.dcycle.io/v1/files/presigned-urls" \
  -H "Authorization: Bearer ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "x-partner: dcycle" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "name": "invoice_january",
      "extension": "pdf",
      "mime_type": "application/pdf",
      "size_kb": 2048
    }
  ]'

Response Fields

id
string
File UUID stored in PostgreSQL.
status
string
Starts as pending.
url
string
Final S3-backed file URL stored on the File record.
presigned_url
string
Temporary S3 PUT URL used for the direct upload.

Step 2: Upload to S3

Upload the raw file bytes to the presigned_url returned in step 1.
curl -X PUT "${PRESIGNED_URL}" \
  -H "Content-Type: application/pdf" \
  --data-binary @invoice_january.pdf

Step 3: Confirm the Upload

PATCH /v1/files/batch-update is the step that flips the file from pending to uploaded and triggers document classification.

Body

file_ids
string[]
required
File IDs returned by step 1.
status
string
required
Use uploaded after a successful S3 upload, or error if the upload failed.
project_id
string
Optional project UUID. When present, the backend also creates file_project links.

Example

curl -X PATCH "https://api.dcycle.io/v1/files/batch-update" \
  -H "Authorization: Bearer ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "x-partner: dcycle" \
  -H "Content-Type: application/json" \
  -d '{
    "file_ids": ["11111111-1111-1111-1111-111111111111"],
    "status": "uploaded"
  }'

Event Trigger

When status=uploaded, the backend emits CLASSIFY_DOCUMENT with:
{
  "resource": "POST",
  "method": "/classify-document",
  "file_id": "file-uuid",
  "user_id": "user-uuid",
  "organization_id": "organization-uuid"
}

Legacy Alternative

If you do not need the direct-to-S3 flow, POST /v1/files/upload still accepts a standard multipart upload through the backend.