Skip to content

Files and attachments

The attachments API separates metadata reads from file access. List and get operations never expose an internal storage path or download credential. When you need the bytes, request a short-lived bearer URL for one attachment.

GET /v1/attachments requires both entity_type and entity_id:

Terminal window
curl "https://api.agencytitan.com/v1/attachments?entity_type=task&entity_id=11111111-1111-4111-8111-111111111111&sort=created_at&order=desc" \
--header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
--header "Accept: application/json"

The operation supports the standard limit, offset, sort, and order parameters plus case-insensitive search over filename and content type. The current entity_type enum is published in the operation schema; use that enum instead of sending an arbitrary resource name.

Before returning metadata, AgencyTitan reads the parent resource through its normal public authorization path. If the caller cannot read the parent, it cannot enumerate the parent’s attachments.

Each item contains:

  • id, used by metadata and download-URL operations
  • filename, content_type, and size_bytes
  • created_at and nullable created_by
  • parent_entity.type and parent_entity.id

Use GET /v1/attachments/{id} when you already know the attachment ID. AgencyTitan re-evaluates read access to the parent before returning the same metadata shape. The response does not contain file bytes or a URL.

POST /v1/attachments/{id}/download-url mints a short-lived bearer credential. It uses POST because it issues a credential, although it does not change product data.

Terminal window
curl --request POST "https://api.agencytitan.com/v1/attachments/22222222-2222-4222-8222-222222222222/download-url" \
--header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
--header "Content-Type: application/json" \
--data '{"expires_in": 300}'

expires_in is measured in seconds. It defaults to 300, with a minimum of 60 and a maximum of 3600.

The response uses the standard single-item envelope:

{
"data": {
"url": "https://storage.example.com/signed/file",
"expires_at": "2026-08-08T14:35:00.000Z",
"filename": "kickoff-notes.pdf",
"content_type": "application/pdf",
"size_bytes": 24576
}
}

Read access to the parent is checked again when the URL is issued. The returned URL is transferable and grants temporary access without the AgencyTitan authorization header, so treat it as a secret:

  • Request it only when the download is ready to begin.
  • Use the shortest practical TTL.
  • Do not log, persist, cache, or send it to an untrusted client.
  • Issue a new URL after expires_at; do not attempt to refresh or modify the signed URL.
  • If parent access is revoked after issuance, assume the URL can remain usable until its TTL expires.

Attachment references in GET /v1/ticket-messages contain metadata only. Pass the referenced attachment ID to the download-URL operation when the caller is ready to fetch it.