Upload a file to a MEDIA Custom Field

This guide shows how to upload a file to a Custom Field of type MEDIA on any DJUST entity (for example: Commercial Order, Logistic Order, Operation, Quote, Product) using a presigned URL flow.

At a glance

  1. Authenticate
  2. Read the entity to find the MEDIA CF id
  3. Request a presigned URL
  4. PUT the file to that URL
  5. PATCH the entity with the file URL as the custom field value.

🔑 Prerequisites

  • Headers: use the correct surface headers for your endpoint (for admin, dj-client=OPERATOR plus dj-api-key; for shop surfaces, dj-client=ACCOUNT where applicable).
  • Permissions: you must be allowed to read and patch the target entity.
  • A MEDIA CF exists on the entity (type MEDIA, with properties like mediaType, maxSize).
  • File size and type are within limits (see the Custom Field’s property maxSize; set a correct Content-Type on upload).

This flow updates a value for an existing custom field. It does not create CF definitions.


🗺️ End-to-end flow

flowchart LR
  A[🔐 Authenticate]:::node --> B[📦 Read entity<br/>find MEDIA CF id]:::node
  B --> C[🔗 Request presigned URL<br/>by MEDIA CF id]:::node
  C --> D[⬆️ PUT file to presigned URL<br/><i>no auth header</i>]:::success
  D --> E[🧩 PATCH entity<br/>customFieldValues with file URL]:::success

  %% Styles
  classDef node fill:#f2f4f7,stroke:#475569,stroke-width:2px,color:#111827;
  classDef success fill:#ecfdf5,stroke:#10b981,stroke-width:2px,color:#064e3b;

  style A rx:8,ry:8
  style B rx:8,ry:8
  style C rx:8,ry:8
  style D rx:8,ry:8
  style E rx:8,ry:8

🔐 Authenticate

curl -X POST "$BASE_URL/auth/token" \
  -H "Content-Type: application/json" \
  -H "dj-api-key: $DJ_API_KEY" \
  -H "dj-client: OPERATOR" \
  -d '{
    "username": "your_username",
    "password": "your_password"
  }'

Response (excerpt)

{ "token": { "accessToken": "eyJhbGciOi..." } }

📦 Read the entity and find the MEDIA CF id

curl -X GET "$BASE_URL/v1/{entityPath}/{entityId}" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "dj-client: OPERATOR" \
  -H "dj-api-key: $DJ_API_KEY" \
  -H "Accept: application/json"

Look for a Custom Field with "type": "MEDIA":

{
  "customFieldValues": [
    {
      "customField": {
        "id": "0000065247",
        "name": { "EN": "CF MEDIA DOC" },
        "type": "MEDIA",
        "properties": { "mediaType": "DOCUMENT", "maxSize": 11 }
      },
      "value": null
    }
  ]
}

Keep the customField.id (for example 0000065247) - you will need it to request the presigned URL. Also keep the entire original customFieldValues array; you will send all values back on the PATCH.


🔗 Request a presigned upload URL

# fileName is the target name, fileSize in bytes
curl -X POST "$BASE_URL/v1/custom-fields/{mediaCustomFieldId}/media?fileName=document.txt&fileSize=1000" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "dj-client: OPERATOR" \
  -H "dj-api-key: $DJ_API_KEY" \
  -H "Accept: application/json"

Response: a plain URL (Amazon S3 presigned URL), for example

https://document.example.com/fileUpload-1764933402860.txt?Expires=...&Signature=...

Presigned URLs expire (typ. 24–48h). Request and upload within that window.


⬆️ Upload the file to the presigned URL

curl -X PUT "$PRESIGNED_URL" \
  -H "Content-Type: text/plain" \
  --data-binary @"$FILE_PATH"

• Do not send auth headers on the presigned URL. • Set the correct Content-Type (application/pdf, image/png, etc.). • Use --data-binary for binary files.


🧩 PATCH the entity with the file URL

Send all custom field values back, replacing only the MEDIA one with the uploaded file URL.

curl -X PATCH "$BASE_URL/v1/{entityPath}/{entityId}" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "dj-client: OPERATOR" \
  -H "dj-api-key: $DJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customFieldValues": [
      { "customFieldId": "AAAAAA1111", "customFieldValue": "foo" },
      { "customFieldId": "0000065247", "customFieldValue": "https://document.example.com/fileUpload-1764933402860.txt?..." },
      { "customFieldId": "BBBBBB2222", "customFieldValue": "bar" }
    ]
  }'

Body format rules • Array of { customFieldId, customFieldValue }. • Do not include nested customField objects. • For MEDIA, customFieldValue is the full URL you just uploaded to.


✅ Verify

curl -X GET "$BASE_URL/v1/{entityPath}/{entityId}" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "dj-client: OPERATOR" \
  -H "dj-api-key: $DJ_API_KEY" \
  -H "Accept: application/json"

You should now see the MEDIA CF populated with the uploaded URL.


🧯 Troubleshooting

Symptom or messageLikely causeFix
mediaCustomFieldId not foundNo MEDIA CF on this entityEnsure the CF exists and is enabled for the target entity
Presigned URL expiredUpload performed too lateRequest a new presigned URL and re-upload
PATCH fails (invalid parameter)Wrong body shape (nested customField included)Send only { customFieldId, customFieldValue } pairs
File too largeExceeds properties.maxSizeCheck CF properties and compress or choose a smaller file
Upload 403 or signature errorAltered URL or wrong Content-TypeRe-request URL, do not alter query string, set correct content type
Upload 4xx when auth header presentAuthorization header sent to storageRemove auth headers on the presigned PUT

📋 Summary of calls

StepMethodEndpointNotes
1POST/auth/tokenGet accessToken
2GET/v1/{entityPath}/{entityId}Find the MEDIA CF id
3POST/v1/custom-fields/{mediaCustomFieldId}/mediaQuery params: fileName, fileSize
4PUT{presignedUploadUrl}Upload the file (no auth headers)
5PATCH/v1/{entityPath}/{entityId}Send all customFieldValues back

🧠 Implementation tips

  • Cache CF metadata client side if you use it often.
  • Validate fileSize before requesting the presigned URL.
  • Timeouts: increase for big files (for example curl --max-time 300).
  • Retries: add basic retries on transient network errors.
  • Security: handle presigned URLs as temporary secrets and avoid verbose logging.

🔗 API quicklinks

  • Read entity - GET /v1/{entityPath}/{entityId} (Pick the right path for your entity, for example logistic-orders, commercial-orders, operations, quotes, products.)
  • Get presigned URL for a MEDIA CF - POST /v1/custom-fields/{mediaCustomFieldId}/media?fileName=...&fileSize=...
  • Upload to storage - PUT {presignedUploadUrl} (no auth header)
  • Patch entity CF values - PATCH /v1/{entityPath}/{entityId}