Task Multimedia & PDFs
Retrieve the evidence attached to a task — photos, signatures, sketches, audio, and the PDF of a completed field form. Three read-only operations:
- List attachment metadata for a task (name, type, size, date, notes, storage provider) — no heavy content.
- Download a single file by
media_id, either as a signedlink(default) or asbase64(on demand). - Generate a form's PDF as base64, on demand.
All endpoints require a valid JWT token, API key, and tenant header. See Authentication.
A credential only reaches the tasks its technical user would see in the web app. The same per-task visibility rules apply here (mobile/vehicle, origin isolation, and group visibility). If a task exists but is outside that scope, you get 403 FORBIDDEN — not a 404.
List Attachments
Retrieve the metadata of a task's direct attachments. The content itself is never included — use the download endpoint to fetch a file.
/apidev/v1/tasks/{id}/attachmentsPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Task identifier. Accepts the internal task ID, the service number, or the external ID (see Multi-identifier resolution). |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | No | Filter by attachment class: signature, image, sketch, audio, qr, barcode, or other. Omit for no filter. |
form_id | string | No | Form identifier (opaque BigInt). Reserved for form attachments. Max length 40. See the note below. |
The default listing returns only the task's direct attachments, so form_id and form_name are always null here. Filtering by form_id targets attachments that belong to a form — a path that is not yet implemented, so this filter currently returns an empty list.
Response Fields
| Field | Type | Description |
|---|---|---|
serid | string | Resolved task ID (BigInt as string) |
service_number | string | null | Service number (BigInt as string) |
assistance_number | string | null | Assistance number (BigInt as string) |
attachments | array | Attachment metadata — without file content |
attachments[].media_id | string | File handle for the download endpoint |
attachments[].name | string | null | Display name of the file |
attachments[].type | string | signature, image, sketch, audio, qr, barcode, or other |
attachments[].raw_type | string | null | Only present when type is other — the original capture code |
attachments[].mime_type | string | null | Inferred from the file extension; null if not recognized |
attachments[].size | number | null | File size in bytes |
attachments[].date | string | null | Capture timestamp (no timezone) |
attachments[].notes | string | null | Notes attached to the file |
attachments[].form_id | string | null | Form ID — always null in the direct listing |
attachments[].form_name | string | null | Form name — always null in the direct listing |
attachments[].provider | string | null | Storage provider label (e.g., azure, s3) |
meta.count | number | Number of attachments returned |
The listing never exposes the file's storage path, container, provider reference, or any signed URL. Fetch the content through the download endpoint.
Code Example
- cURL
- JavaScript
- Python
curl -s "https://$TENANT/apidev/v1/tasks/103878/attachments?type=image" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const response = await fetch(
`https://${TENANT}/apidev/v1/tasks/103878/attachments?type=image`,
{
headers: {
"Authorization": `Bearer ${token}`,
"X-API-Key": API_KEY,
"tenant": TENANT,
},
}
);
const { data, meta } = await response.json();
console.log(`Task ${data.service_number} has ${meta.count} attachment(s)`);
response = requests.get(
f"https://{TENANT}/apidev/v1/tasks/103878/attachments",
headers=headers,
params={"type": "image"},
)
result = response.json()
for media in result["data"]["attachments"]:
print(f"{media['media_id']}: {media['name']} ({media['type']})")
Example Response
{
"success": true,
"data": {
"serid": "1284773829100",
"service_number": "103878",
"assistance_number": "5567",
"attachments": [
{
"media_id": "9981273645",
"name": "foto_siniestro_1.jpg",
"type": "image",
"mime_type": "image/jpeg",
"size": 482113,
"date": "2026-06-22T14:31:07",
"notes": "Frente del vehículo",
"form_id": null,
"form_name": null,
"provider": "azure"
},
{
"media_id": "9981273988",
"name": "conformidad_firma.png",
"type": "signature",
"mime_type": "image/png",
"size": 18422,
"date": "2026-06-22T15:02:44",
"notes": null,
"form_id": null,
"form_name": null,
"provider": "s3"
}
]
},
"meta": { "count": 2 }
}
Download Attachment
Fetch a single file by media_id. By default you get a short-lived signed URL (mode=link); pass mode=base64 to receive the full content inline.
/apidev/v1/tasks/{id}/attachments/{media_id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Task identifier (task ID, service number, or external ID) |
media_id | string | Yes | Attachment identifier from the List Attachments response |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | No | link | link returns a signed, expiring URL. base64 returns the full file content. |
Response Fields
| Field | Type | Present in | Description |
|---|---|---|---|
serid | string | both | Resolved task ID |
service_number | string | null | both | Service number |
assistance_number | string | null | both | Assistance number |
media_id | string | both | Echo of the requested attachment ID |
name | string | null | both | File name |
type | string | both | Attachment class (see List Attachments) |
mime_type | string | null | both | MIME type. With base64, the storage provider's reported type is preferred over the extension guess. |
size | number | null | both | File size in bytes |
mode | string | both | Echo of the mode — link or base64 |
download_url | string | link only | Short-lived signed URL |
expires_at | string | null | link only | When the signed URL stops working (no timezone) |
file_base64 | string | base64 only | Full file content |
A link URL is ephemeral. Download the content before expires_at. For bulk archiving, call this endpoint again to refresh the URL. An expired link is never returned — if the link can't be refreshed you get 409 ATTACHMENT_UNAVAILABLE instead of a dead URL.
link for bulkbase64 loads the entire file into memory. Use it for small files or one-off fetches; for large files or bulk downloads, prefer mode=link.
Code Example
- cURL
- JavaScript
- Python
# Default — signed link
curl -s "https://$TENANT/apidev/v1/tasks/103878/attachments/9981273645" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
# Base64 content, on demand
curl -s "https://$TENANT/apidev/v1/tasks/103878/attachments/9981273988?mode=base64" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const response = await fetch(
`https://${TENANT}/apidev/v1/tasks/103878/attachments/9981273645?mode=link`,
{ headers }
);
const { data } = await response.json();
console.log(`Download ${data.name} before ${data.expires_at}: ${data.download_url}`);
response = requests.get(
f"https://{TENANT}/apidev/v1/tasks/103878/attachments/9981273988",
headers=headers,
params={"mode": "base64"},
)
data = response.json()["data"]
print(f"{data['name']}: {data['size']} bytes, {len(data['file_base64'])} base64 chars")
Example Response — mode=link
{
"success": true,
"data": {
"serid": "1284773829100",
"service_number": "103878",
"assistance_number": "5567",
"media_id": "9981273645",
"name": "foto_siniestro_1.jpg",
"type": "image",
"mime_type": "image/jpeg",
"size": 482113,
"download_url": "https://geotareasstore.blob.core.windows.net/cia-42/9981273645.jpg?sv=2024-08&se=2026-06-25T16%3A10%3A00Z&sig=Rb9...",
"expires_at": "2026-06-25T16:10:00",
"mode": "link"
}
}
Example Response — mode=base64
{
"success": true,
"data": {
"serid": "1284773829100",
"service_number": "103878",
"assistance_number": "5567",
"media_id": "9981273988",
"name": "conformidad_firma.png",
"type": "signature",
"mime_type": "image/png",
"size": 18422,
"file_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
"mode": "base64"
}
}
Form PDF
Generate the PDF of a completed form on the task. The PDF is produced on demand and returned as base64 — it is not stored.
/apidev/v1/tasks/{id}/forms/{form_id}/pdfPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Task identifier (task ID, service number, or external ID) |
form_id | string | Yes | Identifier of the completed form (opaque BigInt) |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | No | base64 | Delivery mode. Only base64 is supported for now. |
Response Fields
| Field | Type | Description |
|---|---|---|
serid | string | Resolved task ID |
service_number | string | null | Service number |
assistance_number | string | null | Assistance number |
form_id | string | Echo of the requested form ID |
form_name | string | null | Human-readable form name |
filename | string | Suggested PDF filename |
mime_type | string | Always application/pdf |
file_base64 | string | The generated PDF content |
mode | string | Always base64 |
Code Example
- cURL
- JavaScript
- Python
curl -s "https://$TENANT/apidev/v1/tasks/103878/forms/77120033/pdf" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const response = await fetch(
`https://${TENANT}/apidev/v1/tasks/103878/forms/77120033/pdf`,
{ headers }
);
const { data } = await response.json();
// Decode and persist on your side
const bytes = Buffer.from(data.file_base64, "base64");
console.log(`${data.filename}: ${bytes.length} bytes`);
import base64
response = requests.get(
f"https://{TENANT}/apidev/v1/tasks/103878/forms/77120033/pdf",
headers=headers,
)
data = response.json()["data"]
with open(data["filename"], "wb") as f:
f.write(base64.b64decode(data["file_base64"]))
print(f"Saved {data['filename']}")
Example Response
{
"success": true,
"data": {
"serid": "1284773829100",
"service_number": "103878",
"assistance_number": "5567",
"form_id": "77120033",
"form_name": "Acta de conformidad",
"filename": "acta_conformidad_103878.pdf",
"mime_type": "application/pdf",
"file_base64": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlL1BhZ2UvUGFyZW50...",
"mode": "base64"
}
}
Multi-identifier resolution
The {id} path parameter accepts any of three identifiers, tried in this order:
- Task ID (
serid) - Service number (
service_number) - External ID (
external_id)
The same value is tried against each candidate within your tenant until one matches a task. Active and historic tasks are both searched, so a finished task still resolves. If none of the three matches, you get 404 TASK_NOT_FOUND.
This means you can hit the same endpoint with whichever identifier you have on hand — the internal task ID, the service number printed on a ticket, or the external ID from your own system.
Errors
| Code | HTTP | Applies to | Description |
|---|---|---|---|
UNAUTHORIZED | 401 | All | Missing, invalid, or expired tenant / Authorization / X-API-Key |
FORBIDDEN | 403 | All | The credential lacks APICLI_TASKS_READ, or the task is outside the technical user's scope |
TASK_NOT_FOUND | 404 | All | No task matches the identifier sent in {id} |
ATTACHMENT_NOT_FOUND | 404 | Download | The task has no attachment with that media_id |
FORM_NOT_FOUND | 404 | Form PDF | The task has no form with that form_id |
INVALID_MODE | 400 | Download, Form PDF | mode is not one of the supported values (link/base64 for download, base64 for PDF) |
ATTACHMENT_UNAVAILABLE | 409 | Download | The file exists in the database but couldn't be resolved in storage (expired link that couldn't refresh, or the file was moved/removed) |
PDF_GENERATION_FAILED | 422 | Form PDF | The form exists but the PDF couldn't be generated |
RATE_LIMITED | 429 | All | Exceeded 30 req/min |
INTERNAL_ERROR | 500 | All | Unexpected server error |
Example Error — task outside scope
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Esta credencial no tiene acceso a los archivos de esta tarea.",
"hint": "Usá una credencial con acceso a esa tarea, o pedí el permiso de lectura de tareas."
}
}
Example Error — file not in storage
{
"success": false,
"error": {
"code": "ATTACHMENT_UNAVAILABLE",
"message": "El archivo existe pero no se pudo recuperar del almacenamiento en este momento.",
"hint": "Reintentá más tarde; si persiste, el archivo puede haberse eliminado del almacenamiento."
}
}
Related
- Tasks — List & Detail — Read tasks and inspect their forms
- Authentication
- Rate Limits
- Error Handling