Start an attachment upload
POST
/v1/tasks/{task_id}/attachments/uploadsPhase 1 of the two-phase upload (the SAME presign flow the in-app uploader uses): declares the file and answers a short-lived presigned PUT URL plus the `file_key` to register afterwards. Checked against the plan's file-size and storage allowances. Nothing is attached until phase 2. Supports the optional `Idempotency-Key` header (a replay returns the SAME URL, which may have expired — start a fresh upload instead of replaying old keys).
Required scopes
tasks:write
Availability
All plans
Quota cost
1 API call
Idempotency
Optional Idempotency-Key
Request
Path parameters
| Field | Type | Description |
|---|---|---|
task_idrequired | string | Task short id, e.g. `T-123` (a bare `123` is also accepted on input).e.g. T-123 |
Request body
| Field | Type | Description |
|---|---|---|
file_namerequired | string | e.g. screenshot.png |
content_typerequired | string | e.g. image/png |
sizerequired | integer | File size in bytes — checked against the organization's plan file-size and storage allowances. |
▸ ▾ Raw request schema
{
"type": "object",
"properties": {
"file_name": {
"type": "string",
"minLength": 1,
"maxLength": 255,
"example": "screenshot.png"
},
"content_type": {
"type": "string",
"minLength": 1,
"maxLength": 255,
"example": "image/png"
},
"size": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "File size in bytes — checked against the organization's plan file-size and storage allowances."
}
},
"required": [
"file_name",
"content_type",
"size"
],
"description": "What you are about to upload."
}Response 201
Where to PUT the file.
Fields
| Field | Type | Description |
|---|---|---|
upload_urlrequired | string | Presigned URL — PUT the raw file bytes here (Content-Type must match what you declared). |
upload_methodrequired | string | |
file_keyrequired | string | Opaque storage key; pass it back when registering the attachment after the upload succeeds. |
expires_atrequired | string (date-time) | When the upload URL stops working, ISO-8601 UTC. |
▸ ▾ Raw response schema
{
"type": "object",
"properties": {
"upload_url": {
"type": "string",
"description": "Presigned URL — PUT the raw file bytes here (Content-Type must match what you declared)."
},
"upload_method": {
"type": "string",
"const": "PUT"
},
"file_key": {
"type": "string",
"description": "Opaque storage key; pass it back when registering the attachment after the upload succeeds."
},
"expires_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the upload URL stops working, ISO-8601 UTC."
}
},
"required": [
"upload_url",
"upload_method",
"file_key",
"expires_at"
],
"additionalProperties": false,
"description": "Phase 1 of the two-phase upload: where to PUT the file."
}Example request
curl
curl -X POST https://app.nowgetitdone.com/v1/tasks/T-123/attachments/uploads \
-H "Authorization: Bearer gid_YOUR_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"file_name": "screenshot.png",
"content_type": "image/png",
"size": 1
}'Possible errors
- 400
validation_failedRequest validation failed - 401
missing_credentialsMissing credentials - 401
invalid_api_keyInvalid API key - 401
invalid_tokenInvalid access token - 403
insufficient_scopeInsufficient scope - 403
feature_not_enabledFeature not enabled on this plan - 404
resource_not_foundResource not found - 409
idempotency_in_progressA request with this Idempotency-Key is still in progress - 422
idempotency_key_reusedIdempotency-Key reused with a different request - 429
rate_limitedRate limit exceeded - 429
quota_exhaustedPlan quota exhausted - 500
internal_errorInternal server error