Day Files
Prepare a short-lived S3 upload, PUT the raw file with the returned headers, then complete the upload so RoadOps can verify and create the day file.
Ask
Upload workflow
Day file uploads use a three-step workflow so the file bytes travel directly to private S3 storage instead of passing through the Developer API:
- Step 1: Call
POST /v1/teams/{teamId}/days/{dayId}/fileswith the file name, MIME type, and exact byte size. The response reserves afileIdand returnsupload.url,upload.headers,upload.expiresAt,completion.path, andcompletion.body. - Step 2: Send the raw file bytes to
upload.urlwithPUT. Include every entry fromupload.headersexactly as returned. Do not usemultipart/form-data, and do not send the RoadOpsAuthorizationheader to S3. S3 must return a successful 2xx response before you continue. - Step 3: Call
POST completion.pathwith your RoadOps bearer token and the returnedcompletion.body. You may add optional visibility JSON alongside the requiredfileName. RoadOps verifies the stored object before accepting the day-file creation command. Use anIdempotency-KeyUUID when retrying this completion request.
The presigned S3 URL is valid for 15 minutes and is limited to the reserved file, MIME type, byte length, and RoadOps metadata. Its expiration controls when S3 can accept the PUT; it does not delete an upload that S3 accepted before expiration. If the URL expires before the upload succeeds, repeat step 1. Supply and reuse an id in the prepare request when you want the replacement URL to target the same not-yet-completed file ID.
# Step 1: prepare the upload
curl -X POST "https://api.roadops.app/v1/teams/{teamId}/days/{dayId}/files" \
-H "Authorization: Bearer ro_live_..." \
-H "Content-Type: application/json" \
-d '{"fileName":"Security map.pdf","contentType":"application/pdf","sizeBytes":245760}'
# Step 2: PUT the raw bytes to upload.url with every returned upload header
curl -X PUT "{upload.url}" \
--upload-file "Security map.pdf" \
-H "Content-Type: application/pdf" \
-H "Content-Length: 245760" \
-H "x-amz-meta-roadops-team-id: {returned value}" \
-H "x-amz-meta-roadops-day-id: {returned value}" \
-H "x-amz-meta-roadops-file-id: {returned value}" \
-H "x-amz-meta-roadops-file-name: {returned value}"
# Step 3: complete the upload after S3 returns 2xx
curl -X POST "https://api.roadops.app{completion.path}" \
-H "Authorization: Bearer ro_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 00000000-0000-4000-8000-000000000999" \
-d '{"fileName":"Security map.pdf","visibility":{"userIds":["00000000-0000-4000-8000-000000000101"]}}'List day files
GET/v1/teams/{teamId}/days/{dayId}/filesLists files attached to a day.
Required scopes: day_file:read
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
query.limit | integerMaximum number of records to return. Defaults to 50 and is capped at 200. |
query.cursor | stringCursor returned by page.nextCursor from the previous list response. |
Response 200
data | object[] |
|---|---|
data[].dayId | string:uuid |
data[].fileName | string |
data[].contentType | string |
data[].sizeBytes | integer |
data[].visibility | object |
data[].visibility.groupIds | string:uuid[] |
data[].visibility.userIds | string:uuid[] |
data[].id | string:uuid |
data[].orgId | string:uuid |
data[].isDeleted | boolean |
data[].version | integerCurrent day file aggregate version. |
data[].createdAt | string:date-timeISO 8601 timestamp when the day file was created. |
data[].createdBy | stringUser identifier that created the day file. |
data[].updatedAt | string:date-timeISO 8601 timestamp when the day file was last updated. |
data[].updatedBy | stringUser identifier that last updated the day file. |
data[].deletedAt | string:date-timeISO 8601 timestamp when the day file was deleted. |
data[].cursor | integerLatest event-ledger cursor projected into this day file record. |
page | object |
page.limit | integerMaximum number of records returned in this page. |
page.nextCursor | string | nullCursor to pass as the next request's cursor query parameter, or null when there are no more results. |
meta | object |
meta.cursor | number | nullHighest record cursor represented by this response. |
Prepare a day file upload
POST/v1/teams/{teamId}/days/{dayId}/filesStep 1 of the day file upload workflow. Send JSON metadata to reserve a file ID and receive a presigned S3 URL that is valid for 15 minutes. Step 2: send the raw file bytes—not multipart/form-data—to upload.url with PUT, including every header in upload.headers exactly as returned. Do not send the RoadOps Authorization header to S3. After S3 returns a successful 2xx response, complete step 3 by calling the returned completion.path with your RoadOps bearer token and completion.body. If the upload URL expires before S3 accepts the file, call this endpoint again; callers that supplied id can reuse that ID until the upload is completed.
Required scopes: day_file:write
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
Request body (application/json)
id | string:uuidOptional caller-supplied day file ID. Reuse the same ID when requesting a replacement URL for an upload that has not been completed. RoadOps generates one when omitted. |
|---|---|
fileNamerequired | stringOriginal file name shown in RoadOps. Path separators and unsupported characters are removed before the upload target is created. |
contentType | stringMIME type to store with the object. Defaults to application/octet-stream. Send the returned Content-Type header unchanged in the S3 PUT request. |
sizeBytesrequired | integerExact file size in bytes. Day file uploads are limited to 10 MB. The S3 PUT request must send this exact number of bytes. |
Response 200
data | object |
|---|---|
data.fileId | string:uuidID reserved for the day file. |
data.fileName | stringSanitized file name that RoadOps will display. |
data.contentType | stringMIME type that will be stored with the uploaded object. |
data.sizeBytes | integerExact number of bytes expected by the signed upload request. |
data.upload | object |
data.upload.method | "PUT"HTTP method required when sending the file directly to S3. |
data.upload.url | string:uriShort-lived presigned S3 URL. Send the raw file bytes to this URL; do not use multipart/form-data. |
data.upload.expiresAt | string:date-timeISO 8601 time when S3 stops accepting new upload requests through this URL. |
data.upload.expiresInSeconds | integerNumber of seconds the presigned URL remains valid after issuance. |
data.upload.headers | objectComplete set of required headers for the S3 PUT request. Send every header exactly as returned and do not send the RoadOps Authorization header to S3. |
data.upload.headers.Content-Type | stringRequired MIME type signed into the upload URL. Send this exact value. |
data.upload.headers.Content-Length | stringRequired decimal byte length signed into the upload URL. Send this exact value. |
data.upload.headers.x-amz-meta-roadops-team-id | string:uuidRequired signed RoadOps team metadata. Send this exact value. |
data.upload.headers.x-amz-meta-roadops-day-id | string:uuidRequired signed RoadOps day metadata. Send this exact value. |
data.upload.headers.x-amz-meta-roadops-file-id | string:uuidRequired signed RoadOps file metadata. Send this exact value. |
data.upload.headers.x-amz-meta-roadops-file-name | stringRequired signed RoadOps file-name metadata. Send this exact value. |
data.completion | object |
data.completion.method | "POST"HTTP method used to complete the upload with RoadOps. |
data.completion.path | stringDeveloper API path to call after S3 returns a successful 2xx response. |
data.completion.body | objectRequired fields to include in the completion request body. |
data.completion.body.fileName | stringSanitized file name to send unchanged in the completion request body. |
Complete a day file upload
POST/v1/teams/{teamId}/days/{dayId}/files/{fileId}/completeStep 3 of the day file upload workflow. Call this endpoint only after the PUT to the presigned S3 URL returns a successful 2xx response. Send the sanitized fileName returned by the prepare call unchanged in this request body. RoadOps uses it to locate the private object, then verifies its signed RoadOps metadata and the 10 MB size limit before accepting the DayFile.Create command. The presigned URL's 15-minute expiration only limits when S3 can accept the PUT; an object uploaded before expiration can still be completed afterward. Use an Idempotency-Key when retrying this completion request, then poll the receipt's links.refetch URL until the returned cursor reaches the receipt cursor.
Required scopes: day_file:write
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
path.fileIdrequired | string:uuidReserved day file identifier returned by the prepare call. |
header.Idempotency-Key | string:uuidOptional caller-generated UUID used to make mutation retries safe. Reuse the same UUID only when retrying the same request. |
Request body (application/json)
fileNamerequired | stringSanitized file name returned by the prepare call. Send it unchanged so RoadOps can verify the uploaded S3 object. |
|---|---|
visibility | objectOptional visibility rules applied when the uploaded object becomes a day file record. |
visibility.groupIds | string:uuid[] |
visibility.userIds | string:uuid[] |
Response 202
commandId | string:uuidCommand identifier accepted into the RoadOps command ledger. |
|---|---|
resourceId | string:uuidIdentifier of the resource targeted by the command. |
resourceType | stringResource type targeted by the command. |
status | "applied"Command dispatch status. |
aggregateVersion | integerAggregate version produced by the accepted command. |
cursor | integerEvent-ledger cursor for the accepted update. Refetch until returned records reach this cursor. |
links | object |
links.refetch | stringRelative URL to refetch the affected resource. |
Retrieve a day file
GET/v1/teams/{teamId}/days/{dayId}/files/{fileId}Returns one day file by ID.
Required scopes: day_file:read
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
path.fileIdrequired | string:uuidDay file identifier. |
Response 200
data | object |
|---|---|
data.dayId | string:uuid |
data.fileName | string |
data.contentType | string |
data.sizeBytes | integer |
data.visibility | object |
data.visibility.groupIds | string:uuid[] |
data.visibility.userIds | string:uuid[] |
data.id | string:uuid |
data.orgId | string:uuid |
data.isDeleted | boolean |
data.version | integerCurrent day file aggregate version. |
data.createdAt | string:date-timeISO 8601 timestamp when the day file was created. |
data.createdBy | stringUser identifier that created the day file. |
data.updatedAt | string:date-timeISO 8601 timestamp when the day file was last updated. |
data.updatedBy | stringUser identifier that last updated the day file. |
data.deletedAt | string:date-timeISO 8601 timestamp when the day file was deleted. |
data.cursor | integerLatest event-ledger cursor projected into this day file record. |
meta | object |
meta.cursor | integerHighest record cursor represented by this response. |
Update a day file
PATCH/v1/teams/{teamId}/days/{dayId}/files/{fileId}Accepts a command to update day file metadata. File replacement is not supported; delete and upload a new file instead.
Required scopes: day_file:write
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
path.fileIdrequired | string:uuidDay file identifier. |
header.Idempotency-Key | string:uuidOptional caller-generated UUID used to make mutation retries safe. Reuse the same UUID only when retrying the same request. |
Request body (application/json)
expectedVersion | integerOptional aggregate version guard. |
|---|---|
updatesrequired | objectDay file fields to update. |
updates.visibility | object | nullUpdated visibility, or null to clear custom visibility. |
updates.visibility.groupIds | string:uuid[] |
updates.visibility.userIds | string:uuid[] |
Response 202
commandId | string:uuidCommand identifier accepted into the RoadOps command ledger. |
|---|---|
resourceId | string:uuidIdentifier of the resource targeted by the command. |
resourceType | stringResource type targeted by the command. |
status | "applied"Command dispatch status. |
aggregateVersion | integerAggregate version produced by the accepted command. |
cursor | integerEvent-ledger cursor for the accepted update. Refetch until returned records reach this cursor. |
links | object |
links.refetch | stringRelative URL to refetch the affected resource. |
Delete a day file
DELETE/v1/teams/{teamId}/days/{dayId}/files/{fileId}Accepts a command to delete a day file and remove the stored object.
Required scopes: day_file:write
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
path.fileIdrequired | string:uuidDay file identifier. |
header.Idempotency-Key | string:uuidOptional caller-generated UUID used to make mutation retries safe. Reuse the same UUID only when retrying the same request. |
Response 202
commandId | string:uuidCommand identifier accepted into the RoadOps command ledger. |
|---|---|
resourceId | string:uuidIdentifier of the resource targeted by the command. |
resourceType | stringResource type targeted by the command. |
status | "applied"Command dispatch status. |
aggregateVersion | integerAggregate version produced by the accepted command. |
cursor | integerEvent-ledger cursor for the accepted update. Refetch until returned records reach this cursor. |
links | object |
links.refetch | stringRelative URL to refetch the affected resource. |
Download a day file
GET/v1/teams/{teamId}/days/{dayId}/files/{fileId}/downloadRedirects to a short-lived signed URL that downloads the file directly. Follow the redirect or read the Location response header.
Required scopes: day_file:read
Parameters
path.teamIdrequired | string:uuidTeam identifier. |
|---|---|
path.dayIdrequired | string:uuidDay identifier. |
path.fileIdrequired | string:uuidDay file identifier. |