Custom Field Attachment Uploads
Part of: Custom Fields — For creating and configuring Image and File custom fields, see the Custom Fields guide.
When an item or inventory item has an Image or File custom field, the Tagnet REST API lets you upload the file in a single call directly to that field. The server validates the content, stores it in the entity's access scope, links it to the field, and removes the file the field previously held. There are no dangling uploads to clean up.
This guide is for integrations. The Tagnet application manages Image and File fields for you through the UI.
How it works
Each Image or File custom field holds at most one attachment at a time. Think of it as a named slot: uploading to the slot fills it, uploading again replaces the previous file, and clearing the field (setting it to null) removes the file entirely.
The API path makes the relationship explicit:
PUT /api/v1/items/{id}/custom-fields/{fieldKey}/image
PUT /api/v1/items/{id}/custom-fields/{fieldKey}/file
PUT /api/v1/inventory-items/{id}/custom-fields/{fieldKey}/image
PUT /api/v1/inventory-items/{id}/custom-fields/{fieldKey}/file
Use the /image suffix for Image-typed fields and /file for File-typed fields. The server rejects a request if the suffix does not match the field's configured type.
Uploading a file
Send a multipart/form-data request with a single part named file.
Example: upload an image to an item
PUT /api/v1/items/42/custom-fields/SpecPhoto/image
Authorization: Bearer <token>
Content-Type: multipart/form-data; boundary=----TagnetBoundary
------TagnetBoundary
Content-Disposition: form-data; name="file"; filename="product-photo.png"
Content-Type: image/png
<binary content>
------TagnetBoundary--
Example: upload a PDF to an inventory item
PUT /api/v1/inventory-items/17/custom-fields/WarrantyDoc/file
Authorization: Bearer <token>
Content-Type: multipart/form-data; boundary=----TagnetBoundary
------TagnetBoundary
Content-Disposition: form-data; name="file"; filename="warranty.pdf"
Content-Type: application/pdf
<binary content>
------TagnetBoundary--
Success response
HTTP 200 with a JSON body:
{
"attachmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "product-photo.png",
"contentType": "image/png",
"sizeBytes": 84231,
"href": "/api/v1/attachments/3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
Use href to download or display the file later.
Permissions
| Entity | Required permission |
|---|---|
| Items | ManageItems (ApiIntegration role or equivalent) |
| Inventory items | EditInventory (ApiIntegration role or equivalent) |
Read-only API keys (ApiClient role) cannot upload attachments.
What the server validates
Before storing the file the server checks:
| Check | Detail |
|---|---|
| Field exists | {fieldKey} must be a custom field defined on the entity's item type |
| Field type matches path | /image only accepts Image-typed fields; /file only accepts File-typed fields |
| File extension | Must be in the allowed set for the field type (see limits below) |
| File size | Must not exceed the limit for the field type |
| Content integrity | The server reads the first few bytes (magic bytes) and rejects a file whose content does not match its extension (a PDF renamed to .png is rejected) |
| Access scope write access | The caller must have write access to the entity's access scope |
A file that fails any check returns HTTP 400 with an error message. The previously stored file is unchanged.
File type limits
| Field type | Allowed extensions | Max size |
|---|---|---|
| Image | .jpg .jpeg .png .gif .webp | 1 MB |
| File | .pdf .doc .docx .xls .xlsx .txt .csv | 2 MB |
Reading attachment metadata from entity responses
You do not need a separate lookup to render an Image or File field. When you fetch an item or inventory item, the response includes a customFieldAttachments map alongside customFields. Each entry in the map corresponds to a field key and carries the same metadata that the upload response returns.
Example item response (trimmed)
{
"id": 42,
"itemId": "WIDGET-001",
"customFields": {
"SpecPhoto": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"customFieldAttachments": {
"SpecPhoto": {
"attachmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "product-photo.png",
"contentType": "image/png",
"sizeBytes": 84231,
"href": "/api/v1/attachments/3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
}
customFields holds the raw attachment ID (a GUID string). customFieldAttachments holds the resolved metadata keyed by the same field key. Use the href from customFieldAttachments to render an <img> tag or link to a download.
The map is omitted from the response when the entity has no Image or File fields with a value set.
Downloading and displaying attachments
Download or display an attachment by sending a GET request to the href from the upload response or the sidecar:
GET /api/v1/attachments/3fa85f64-5717-4562-b3fc-2c963f66afa6
Authorization: Bearer <token>
Images and PDFs are served inline (Content-Disposition: inline), so a browser or mobile client can display them directly. Other file types are served as a download (Content-Disposition: attachment).
Replacing a file
Uploading to a field that already holds a file replaces it. The call is idempotent with respect to the slot: PUT to the same URL with a new file and the server stores the new file, links it to the field, and deletes the old file. No orphaned blobs are left behind.
Removing a file (clearing the field)
There is no standalone delete endpoint for attachment files. To remove a file, clear the field by sending a PUT to the entity with the field set to null:
PUT /api/v1/items/42
Content-Type: application/json
Authorization: Bearer <token>
{
"customFields": {
"SpecPhoto": null
}
}
The server removes the underlying file when it detects the field no longer references it. Deleting the entity itself has the same effect: all attachment files the entity's fields referenced are removed.
Error responses
| HTTP status | Meaning |
|---|---|
| 400 | Missing file, unknown field key, type mismatch, invalid extension, file too large, or content does not match extension |
| 403 | Caller lacks the required permission or write access to the entity's access scope |
| 404 | Entity not found |
| 500 | Storage failure (upload rollback is performed; the field is unchanged) |
Frequently Asked Questions
Q: Can I upload multiple files to one field?
A: No. Each Image or File custom field holds one attachment at a time. If you need to attach multiple files, define multiple fields (for example, Photo1, Photo2) or restructure your data model.
Q: Does the upload endpoint require an existing value in the field? A: No. You can upload to a field that has never had a value. The slot starts empty and the upload fills it.
Q: What happens if the upload succeeds but the field update fails? A: The server rolls back the newly uploaded file automatically. The field remains unchanged and no orphaned blob is created.
Q: Can I use an API key to upload attachments? A: Yes, provided the key has the ApiIntegration role and is assigned to the entity's access scope. ApiClient (read-only) keys cannot upload.
Q: How do I know which attachment ID a field holds?
A: The customFields map in the entity response holds the raw GUID for each Image or File field that has a value. The customFieldAttachments sidecar in the same response resolves that GUID to filename, content type, size, and a download URL.