Pushing documents
Pushing means the other system sends each document to DDM itself, by its own reference. The same document sent again is the same document in DDM: unchanged bytes do nothing, new bytes become a new version.
1. Set up a push source
Under Settings › External sources, click New source and choose the system type Push (external API). A push source has nothing to connect to. Its settings decide how pushed documents are filed, exactly as for a connector:
| Setting | Effect on pushed documents |
|---|---|
| Category mapping | Mirror the folder structure builds the categoryPath you send as a category tree. Everything into one category makes one top-level category named after the whole path (Products / Price lists). Fixed category files everything into the category chosen, whatever is sent |
| Import categories | Off: no categories are created from categoryPath |
| Already present | When a document with this reference exists: Store as a new version (default), Overwrite (replace the current version) or Skip (leave it alone) |
| File filter | e.g. *.pdf – other names are skipped |
| Group mappings | A groupName you send becomes read access for the mapped DDM group |
Switch the source on. Pushes to a source that is switched off are refused with 403.
See External sources for these settings in detail.
2. Create a key that writes
Under Settings › API access, create a key with Write and choose the push source. Pushed documents belong to that source, and their references are unique within it. Let the key act as a person who may create documents in the target categories – see Authentication.
3. Push a document
PUT /documents/ref/{ref} creates the document or updates it. Send the file as multipart, with the details as JSON in a metadata part:
curl -X PUT "https://ddm.example.com/api/ext/v1/documents/ref/PIM-4711" \
-H "Authorization: Bearer $DDM_API_KEY" \
-F "file=@Preisliste-2026.pdf;type=application/pdf" \
-F 'metadata={"categoryPath":"Products/Price lists","tags":["Prices","2026"],"titles":{"DE":"Preisliste 2026","AT":"Preisliste 2026"}};type=application/json'
import { readFile } from 'node:fs/promises'
const form = new FormData()
form.append('file', new Blob([await readFile('Preisliste-2026.pdf')], { type: 'application/pdf' }), 'Preisliste-2026.pdf')
form.append('metadata', JSON.stringify({
categoryPath: 'Products/Price lists',
tags: ['Prices', '2026'],
titles: { DE: 'Preisliste 2026', AT: 'Preisliste 2026' }
}))
const response = await fetch(`https://ddm.example.com/api/ext/v1/documents/ref/${encodeURIComponent('PIM-4711')}`, {
method: 'PUT',
headers: { Authorization: `Bearer ${process.env.DDM_API_KEY}` },
body: form
})
const { outcome, document } = await response.json()
import json, os, requests
from urllib.parse import quote
with open("Preisliste-2026.pdf", "rb") as file:
response = requests.put(
f"https://ddm.example.com/api/ext/v1/documents/ref/{quote('PIM-4711', safe='')}",
headers={"Authorization": f"Bearer {os.environ['DDM_API_KEY']}"},
files={
"file": ("Preisliste-2026.pdf", file, "application/pdf"),
"metadata": (None, json.dumps({
"categoryPath": "Products/Price lists",
"tags": ["Prices", "2026"],
"titles": {"DE": "Preisliste 2026", "AT": "Preisliste 2026"},
}), "application/json"),
},
)
result = response.json()
$form = @{
file = Get-Item "Preisliste-2026.pdf"
metadata = '{"categoryPath":"Products/Price lists","tags":["Prices","2026"]}'
}
Invoke-RestMethod -Method Put -Form $form `
-Uri "https://ddm.example.com/api/ext/v1/documents/ref/PIM-4711" `
-Headers @{ Authorization = "Bearer $env:DDM_API_KEY" }
The answer says what happened, and shows the document:
{
"outcome": "created",
"document": {
"id": "8d0c1f5e-6c43-4f0e-9a52-0b7f1a2c3d4e",
"externalRef": "PIM-4711",
"name": "Preisliste-2026.pdf",
"status": "draft",
"tags": ["2026", "Prices"],
"titles": { "DE": "Preisliste 2026", "AT": "Preisliste 2026" },
"categoryIds": ["2b5e…"],
"currentVersion": { "versionNo": 1, "checksum": "9f86d0…", "sizeBytes": 48211 }
}
}
outcome | Status | Meaning |
|---|---|---|
created | 201 | A new document |
versioned | 200 | New bytes on an existing document – a new version, or replaced under Overwrite |
unchanged | 200 | The same bytes as the current version – nothing changed |
skipped | 200 | The file filter left it out, or the document exists and Already present is Skip. document is null |
The details (metadata)
All optional:
| Field | Meaning |
|---|---|
name | The file name, with its extension. Default: the uploaded file's name |
categoryPath | e.g. Products/Price lists. Missing categories are created, according to the category mapping |
mimeType | Overrides the file's declared type |
tags | Replaces the document's tags |
titles | Titles per market code, merged with the existing ones |
groupName | An external group, mapped by the source's group mapping |
Or: from a URL
Instead of uploading, send JSON with a fileUrl. DDM downloads the file itself:
curl -X PUT "https://ddm.example.com/api/ext/v1/documents/ref/PIM-4711" \
-H "Authorization: Bearer $DDM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fileUrl":"https://pim.example.com/export/4711.pdf","categoryPath":"Products/Price lists"}'
The address has to be reachable from DDM over HTTP(S), and redirects are not followed. The file may be as large as an upload – the plan's maximum file size – and DDM writes it to disk as it arrives. The download may take up to 15 minutes. If it fails, the answer is 422 with the reason; if the file is too large, 413.
DDM never contacts cloud metadata addresses. On a hosted DDM, whoever runs it may also block addresses in private networks; the file then has to be reachable from outside.
4. Change and delete
PATCH /documents/ref/{ref}changesname,status(archivedordraft),shareable,highlighted,tags,titlesorcategoryIdswithout sending the file again.DELETE /documents/ref/{ref}deletes the document, exactly as deleting it in the CMS would. A document under a retention hold, or in a running approval, is refused with409.
curl -X PATCH "https://ddm.example.com/api/ext/v1/documents/ref/PIM-4711" \
-H "Authorization: Bearer $DDM_API_KEY" -H "Content-Type: application/json" \
-d '{"status":"archived"}'
5. Categories, if you manage them yourself
If the other system has its own tree, push it top down by reference. parentRef names the parent by its reference:
curl -X PUT "https://ddm.example.com/api/ext/v1/categories/ref/CAT-10" \
-H "Authorization: Bearer $DDM_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Products"}'
curl -X PUT "https://ddm.example.com/api/ext/v1/categories/ref/CAT-11" \
-H "Authorization: Bearer $DDM_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Price lists","parentRef":"CAT-10","titles":{"DE":"Preislisten","AT":"Preislisten"}}'
Then file documents with categoryIds (PATCH), or set the push source's category mapping to Fixed category.
What happens after a push
A pushed document is a document like any other:
- It starts with the organisation's default status for uploads – or the status the push source sets under Status of new documents. If the source is trusted, its documents and new versions are approved at once (more).
- New bytes on a document under review end that review, as an upload in the CMS does.
- People who worked on it get the usual "New version" e-mail.
- With Distribute only after approval, readers see it once it is approved.
DELETE.