Skip to content

File Uploads

CarvOS provides a two-step file workflow: upload files first, then reference them by carv_file_id in your webhook payloads. CarvOS handles promotion to long-term storage automatically when it processes the webhook. See the API Reference for the full upload endpoint schema.

How It Works

  1. Upload -- Send the file to POST /uploads and receive a carv_file_id and expires_at deadline
  2. Reference -- Include the carv_file_id in your webhook payload (e.g., a candidate's resume)
  3. Automatic promotion -- CarvOS promotes the file to long-term storage when processing the webhook

One file, one webhook

Each carv_file_id can only be used in one webhook. Once a webhook references a file, that file is exclusively bound to it. To attach the same document to multiple webhooks (e.g., a candidate and an application), upload the file once per webhook to get separate carv_file_id values.

Tip

Files expire after the expires_at deadline if not referenced in a webhook. Upload files shortly before sending the referencing webhook.

Prerequisites

  • A valid API key (X-API-Key)
  • A client identifier (X-ATS-Client-ID) with a configured workspace mapping

Step 1: Upload a File

  1. Send the file

    Make a POST /uploads request with the file as multipart/form-data:

    curl -X POST "https://api.carvos.io/uploads" \
      -H "X-API-Key: your-api-key" \
      -H "X-ATS-Client-ID: your-client-id" \
      -F "file=@resume.pdf"
    
    Python and JavaScript examples
    import requests
    
    with open("resume.pdf", "rb") as f:
        response = requests.post(
            "https://api.carvos.io/uploads",
            headers={
                "X-API-Key": "your-api-key",
                "X-ATS-Client-ID": "your-client-id",
            },
            files={"file": f},
        )
    
    data = response.json()
    carv_file_id = data["carv_file_id"]
    
    const form = new FormData();
    form.append("file", fileBlob, "resume.pdf");
    
    const response = await fetch("https://api.carvos.io/uploads", {
      method: "POST",
      headers: {
        "X-API-Key": "your-api-key",
        "X-ATS-Client-ID": "your-client-id",
      },
      body: form,
    });
    
    const { carv_file_id } = await response.json();
    
  2. Store the reference ID

    The response includes the carv_file_id you'll use in webhook payloads:

    {
      "carv_file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "file_name": "resume.pdf",
      "content_type": "application/pdf",
      "file_size": 245760,
      "uploaded_at": "2026-03-12T10:30:00Z",
      "expires_at": "2026-03-15T10:30:00Z"
    }
    

    The returned content_type is the MIME type detected from the file's content via magic bytes -- one of application/pdf, application/msword, or application/vnd.openxmlformats-officedocument.wordprocessingml.document. The Content-Type header on the multipart file part is not used.

    Warning

    The file will be automatically deleted at expires_at if not referenced. Send the webhook that uses this file before then.

Step 2: Reference in a Webhook Payload

Include the carv_file_id in the files object of your webhook payload. Each file slot accepts a FileReference with the carv_file_id:

{
  "client_id": "your-client-id",
  "user_id": "user-123",
  "data": {
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane.doe@example.com"
  },
  "files": {
    "resume": {
      "carv_file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }
  }
}

Supported File Slots

Slot Description Used In
resume Candidate resume / CV Candidate, Application webhooks
cover_letter Cover letter Application webhooks

Info

File references are optional. Webhooks without files work normally -- omit the files object entirely if no files are attached.

Step 3: Automatic Promotion

When CarvOS processes a webhook containing a carv_file_id, the referenced file is automatically promoted from staging to long-term storage. No action is required from your side.

If a referenced file has expired (past expires_at), the webhook processing will return an error indicating the file is no longer available. Upload a new file and retry the webhook.

Error Handling

Upload Validation Errors

Status Cause Example
422 Invalid extension File extension '.exe' is not allowed. Allowed extensions: .doc, .docx, .pdf
422 File too large File size exceeds maximum of 52428800 bytes (50MB)
422 Content mismatch File content does not match extension '.pdf': detected 'image/png'

Webhook Processing Errors

Scenario Cause Resolution
File expired Webhook sent after expires_at Upload a new file and retry
File not found Invalid carv_file_id Verify the ID from the upload response
File already consumed carv_file_id was already used in another webhook Upload the file again to get a new carv_file_id

Next Steps