Skip to main content

Overview

Register one or more files with a job to receive signed upload instructions. Plangrep inspects each file’s declared size and SHA-256 digest and returns either a direct PUT URL (for smaller files) or multipart upload instructions (for large files). You then upload bytes directly to the signed storage URLs — bypassing Plangrep’s servers — before calling Complete Uploads. This is the recommended path for large files (e.g. full drawing sets) where multipart upload and direct-to-storage delivery offer better performance and reliability.
The total declared file sizes across all registrations for a job must not exceed 10 GB.

Endpoint

POST https://plangrep.com/api/open/v1/jobs/{jobId}/uploads

Authentication

All requests must include a Bearer token in the Authorization header.
Authorization: Bearer YOUR_API_KEY

Path Parameters

jobId
string
required
The unique identifier of the job to register files against.

Request Body

Content-Type: application/json
files
array
required
List of files to register. Must contain between 1 and 100 items.
files[].fileName
string
required
The original filename, including extension (e.g. large-drawings.pdf).
files[].contentType
string
required
MIME type of the file (e.g. application/pdf).
files[].fileSize
integer
required
Exact size of the file in bytes. Must be >= 0. This value is used to determine upload mode and allocate storage.
files[].sha256
string
required
A 64-character lowercase hex string — the SHA-256 digest of the exact bytes you will upload. Used for integrity verification and deduplication.
files[].classification
string
Document classification hint. One of: plans, specifications, documents, addenda, mixed, other. Omit to allow Plangrep to auto-classify based on file content.

Response

Status: 200 OK
job
object
The updated Job object.
uploads
array
One UploadInstruction object per registered file.
uploads[].documentId
string
The unique document ID assigned to this file. Reference this ID in the completion request.
uploads[].uploadMode
string
The upload strategy Plangrep selected. Either direct_put (single PUT request) or multipart (multiple part uploads).
uploads[].uploadUrl
string
direct_put only. The signed URL to PUT the full file bytes to. Valid until expiresAt.
uploads[].uploadHeaders
object
direct_put only. A map of HTTP headers that must be included when uploading bytes to uploadUrl (e.g. Content-Type, x-amz-checksum-sha256). Omitting these headers will cause the upload to fail.
uploads[].uploadToken
string
An opaque token proving upload completion. Include this in the Complete Uploads request body.
uploads[].expiresAt
string
ISO 8601 timestamp indicating when the signed upload URL(s) expire. Complete the upload before this time.
uploads[].multipartUploadId
string
multipart only. The multipart upload session ID. Include this in the completion request.
uploads[].partSize
integer
multipart only. The target size in bytes for each part. Use this to split the file when uploading parts.
uploads[].parts
array
multipart only. One entry per part, describing the byte range and signed URL for that part.
parts[].partNumber
integer
1-based part index. Include this in the completion request’s parts array.
parts[].startByte
integer
Inclusive start byte offset for this part within the full file.
parts[].endByteExclusive
integer
Exclusive end byte offset for this part. Read bytes [startByte, endByteExclusive).
parts[].uploadUrl
string
Signed URL to PUT this part’s bytes to.
parts[].uploadHeaders
object
Required headers for the PUT request for this part.
documents
array
Additional document metadata objects associated with this registration.

Upload Flow After Registration

Direct PUT

Register → PUT file bytes to uploadUrl (with uploadHeaders)
         → Complete with directUploads: [{ documentId, uploadToken }]
  1. Receive uploadUrl and uploadHeaders for the document.
  2. PUT the complete file bytes to uploadUrl, including every key-value pair from uploadHeaders.
  3. Include { documentId, uploadToken } in the directUploads array of the Complete Uploads request.

Multipart

Register → PUT each part byte range to its parts[n].uploadUrl
         → Collect ETags from each response
         → Complete with multipartUploads: [{ documentId, multipartUploadId, uploadToken, parts }]
  1. For each entry in parts, PUT the corresponding byte range to parts[n].uploadUrl with parts[n].uploadHeaders.
  2. Capture the ETag response header from each part upload.
  3. Include the full multipartUploads record — with parts[].partNumber and parts[].etag — in the Complete Uploads request.
Signed upload URLs expire at expiresAt. If uploads are not completed before expiry, you must re-register the files to obtain fresh URLs. Do not store signed URLs for reuse.

Example Request

curl -X POST https://plangrep.com/api/open/v1/jobs/JOB_ID/uploads \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "files": [
      {
        "fileName": "large-drawings.pdf",
        "contentType": "application/pdf",
        "fileSize": 52428800,
        "sha256": "a3f1b2c4d5e6f7081920a1b2c3d4e5f6a7b8c9d0e1f2031415161718191a1b1c"
      }
    ]
  }'

Example Response (direct_put)

{
  "job": {
    "id": "job_01abc123",
    "status": "uploading"
  },
  "uploads": [
    {
      "documentId": "doc_01xyz789",
      "uploadMode": "direct_put",
      "uploadUrl": "https://storage.example.com/uploads/doc_01xyz789?X-Signature=...",
      "uploadHeaders": {
        "Content-Type": "application/pdf",
        "x-amz-checksum-sha256": "a3f1b2c4..."
      },
      "uploadToken": "tok_abc123def456",
      "expiresAt": "2024-11-01T15:30:00Z"
    }
  ],
  "documents": []
}

Example Response (multipart)

{
  "job": {
    "id": "job_01abc123",
    "status": "uploading"
  },
  "uploads": [
    {
      "documentId": "doc_01xyz789",
      "uploadMode": "multipart",
      "uploadToken": "tok_abc123def456",
      "multipartUploadId": "mpu_def456ghi789",
      "partSize": 10485760,
      "expiresAt": "2024-11-01T15:30:00Z",
      "parts": [
        {
          "partNumber": 1,
          "startByte": 0,
          "endByteExclusive": 10485760,
          "uploadUrl": "https://storage.example.com/uploads/doc_01xyz789/part/1?X-Signature=...",
          "uploadHeaders": { "Content-Type": "application/octet-stream" }
        },
        {
          "partNumber": 2,
          "startByte": 10485760,
          "endByteExclusive": 20971520,
          "uploadUrl": "https://storage.example.com/uploads/doc_01xyz789/part/2?X-Signature=...",
          "uploadHeaders": { "Content-Type": "application/octet-stream" }
        }
      ]
    }
  ],
  "documents": []
}