Skip to content
Seal
Esc
navigateopen⌘Jpreview
On this page

Uploads API

Generate secure upload URLs for document files

Uploads API

The Uploads API provides secure, temporary URLs for uploading document files directly to Seal’s storage. This two-step process ensures secure file handling and prevents exposing storage credentials.

Upload Flow

  1. Generate Upload URL: Request a temporary upload URL from the API
  2. Upload File: POST the file directly to the generated URL and receive a storageId in the response
  3. Create Document: Use the returned storageId as storage_id to create a document

Generate Upload URL

Generate a temporary URL for uploading a document file.

Endpoint

POST /api/v1/uploads/generate-url

Required Scope

seal:documents:write

Request Body

No request body is needed. Simply send an authenticated POST request.

TypeScript Example

const response = await fetch(
  "https://api.seal.nyc/api/v1/uploads/generate-url",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer ak_your_api_key_here",
    },
  }
);

const { upload_url } = await response.json();

cURL Example

curl -X POST https://api.seal.nyc/api/v1/uploads/generate-url \
  -H "Authorization: Bearer ak_your_api_key_here"

Response

{
  "upload_url": "https://api.seal.nyc/api/v1/uploads?token=..."
}

Upload File

Upload the file to the generated URL. The response from Cloudflare R2 storage returns a storageId that you use when creating the document.

Endpoint

POST {upload_url}

Request

  • Method: POST
  • Body: Raw file binary data
  • Headers: Content-Type: {content_type}

TypeScript Example

// Step 1: Generate upload URL
const urlResponse = await fetch(
  "https://api.seal.nyc/api/v1/uploads/generate-url",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer ak_your_api_key_here",
    },
  }
);

const { upload_url } = await urlResponse.json();

// Step 2: Upload file
const fileBlob = await fetch("/path/to/contract.pdf").then((r) => r.blob());

const uploadResponse = await fetch(upload_url, {
  method: "POST",
  body: fileBlob,
  headers: {
    "Content-Type": "application/pdf",
  },
});

if (!uploadResponse.ok) {
  throw new Error("File upload failed");
}

const { storageId } = await uploadResponse.json();

// Step 3: Create document with storage_id
const docResponse = await fetch("https://api.seal.nyc/api/v1/documents", {
  method: "POST",
  headers: {
    Authorization: "Bearer ak_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Service Agreement",
    storage_id: storageId,
    file_size: fileBlob.size,
    file_type: "application/pdf",
  }),
});

const document = await docResponse.json();

cURL Example

# Step 1: Generate upload URL
RESPONSE=$(curl -X POST https://api.seal.nyc/api/v1/uploads/generate-url \
  -H "Authorization: Bearer ak_your_api_key_here")

UPLOAD_URL=$(echo $RESPONSE | jq -r '.upload_url')

# Step 2: Upload file
UPLOAD_RESPONSE=$(curl -X POST "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary "@contract.pdf")

STORAGE_ID=$(echo $UPLOAD_RESPONSE | jq -r '.storageId')

# Step 3: Create document
curl -X POST https://api.seal.nyc/api/v1/documents \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d "{
    \"title\": \"Service Agreement\",
    \"storage_id\": \"$STORAGE_ID\",
    \"file_size\": $(wc -c < contract.pdf),
    \"file_type\": \"application/pdf\"
  }"

Supported File Types

MIME Type Extension Description
application/pdf .pdf PDF documents (recommended)
application/msword .doc Microsoft Word (legacy)
application/vnd.openxmlformats-officedocument.wordprocessingml.document .docx Microsoft Word
image/png .png PNG images
image/jpeg .jpg, .jpeg JPEG images

Error Handling

Upload URL Generation Errors

Status Error Description
401 UNAUTHORIZED Invalid API key
403 INSUFFICIENT_SCOPE Missing seal:documents:write scope

File Upload Errors

Status Error Description
400 FILE_TOO_LARGE File exceeds 50 MB limit
410 URL_EXPIRED Upload URL has expired (generate a new one)

Best Practices

  1. Generate URL Just-In-Time: Create upload URLs immediately before uploading to minimize expiration risk

  2. Validate File Size: Check file size client-side before requesting an upload URL

  3. Handle Expiration: Implement retry logic that generates a new URL if upload fails due to expiration

  4. Verify Upload: Check the upload response status before proceeding to document creation

  5. Store Storage ID: Save the storageId from the upload response for document creation

Complete Upload Example

async function uploadAndCreateDocument(file: File, title: string) {
  try {
    // Step 1: Generate upload URL
    const urlResponse = await fetch(
      "https://api.seal.nyc/api/v1/uploads/generate-url",
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.SEAL_API_KEY}`,
        },
      }
    );

    if (!urlResponse.ok) {
      throw new Error("Failed to generate upload URL");
    }

    const { upload_url } = await urlResponse.json();

    // Step 2: Upload file
    const uploadResponse = await fetch(upload_url, {
      method: "POST",
      body: file,
      headers: {
        "Content-Type": file.type,
      },
    });

    if (!uploadResponse.ok) {
      throw new Error("File upload failed");
    }

    const { storageId } = await uploadResponse.json();

    // Step 3: Create document
    const docResponse = await fetch("https://api.seal.nyc/api/v1/documents", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SEAL_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title,
        storage_id: storageId,
        file_size: file.size,
        file_type: file.type,
      }),
    });

    if (!docResponse.ok) {
      throw new Error("Document creation failed");
    }

    return await docResponse.json();
  } catch (error) {
    console.error("Upload failed:", error);
    throw error;
  }
}

Next Steps

Was this page helpful?