Upload document via REST API

I’ve been reading the docs, comments here, and GitLab on how to upload a document.

Here is what I figured out so far:

curl --location 'http://127.0.0.1:8080/api/v4/documents/upload/' \
--header 'Authorization: Token 5f10c9406d99209a0065a5da4862b86dced18053' \
--form 'document_type_id="4"' \
--form 'file=@"/C:/Users/chris/OneDrive/Documents/Screenshot_1.png"'

I will expand on this once I fully figure out what I am doing.

Here is a NodeRed flow that:

  • Uploads document,
  • Adds document to correct cabinet,
  • Adds metadata value.

Required NodeRed palettes:

  • node-red-dashboard
  • node-red-contrib-ui-upload

Import flow:

[{"id":"f6f2187d.f17ca8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"549c7642da358bdd","type":"http request","z":"f6f2187d.f17ca8","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":750,"y":200,"wires":[["12e98b9c214451b9"]]},{"id":"12e98b9c214451b9","type":"function","z":"f6f2187d.f17ca8","name":"store token","func":"if(msg.statusCode === 200) {\n    node.log('obtain auth token success');\n    flow.set('mayan.auth.token', msg.payload.token);\n    flow.set('mayan.auth.valid', true);\n} else {\n    node.warn('obtain auth token failed!');\n    flow.set('mayan.auth.token', null);\n    flow.set('mayan.auth.valid', false);\n}\nreturn null;","outputs":0,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":950,"y":200,"wires":[]},{"id":"5eaa7606e06ff73a","type":"inject","z":"f6f2187d.f17ca8","name":"run at startup","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":240,"y":100,"wires":[["df6d1478d9c32f01"]]},{"id":"df6d1478d9c32f01","type":"function","z":"f6f2187d.f17ca8","name":"settings","func":"flow.set('mayan.api.url_base', 'http://mayan-app-1:8000/api/v4');\nflow.set('mayan.credential.username', 'admin');\nflow.set('mayan.credential.password', 'wmHMhj5tEa');\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":100,"wires":[["e69bc0c889878581"]]},{"id":"e69bc0c889878581","type":"function","z":"f6f2187d.f17ca8","name":"build request","func":"msg.method = \"POST\";\nmsg.url = `${flow.get('mayan.api.url_base')}/auth/token/obtain/`;\nmsg.headers = {\n    'Content-Type': 'application/json',\n    'Accept': 'application/json'\n};\nmsg.payload = {\n    'username': flow.get('mayan.credential.username'),\n    'password': flow.get('mayan.credential.password')\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":200,"wires":[["549c7642da358bdd"]]},{"id":"4c73c5ff9e205c68","type":"ui_upload","z":"f6f2187d.f17ca8","group":"70994f3107e78413","title":"Upload Invoice Without Purchase Order","accept":"","name":"upload document","order":1,"width":0,"height":5,"chunk":256,"transfer":"binary","x":230,"y":340,"wires":[["26b16b339747d9c1"]]},{"id":"bc9b5396b35ad48c","type":"http request","z":"f6f2187d.f17ca8","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":750,"y":420,"wires":[["ae20ac342ca148c9"]]},{"id":"22a3feb2cf341c76","type":"function","z":"f6f2187d.f17ca8","name":"build request","func":"msg.method = \"POST\";\nmsg.url = `${flow.get('mayan.api.url_base')}/documents/upload/`;\nmsg.headers = {\n    'Content-Type': 'multipart/form-data',\n    'Authorization': `Token ${flow.get('mayan.auth.token')}`\n}\nmsg.payload = {\n  \"document_type_id\": msg.mayan.document_type_id,\n  \"file\": {\n    \"value\": msg.mayan.document__file_buffer,\n    \"options\": {\n      \"filename\": msg.mayan.document__file_name\n    }\n  }\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":420,"wires":[["bc9b5396b35ad48c"]]},{"id":"ae20ac342ca148c9","type":"function","z":"f6f2187d.f17ca8","name":"response","func":"if (msg.statusCode > 299) {\n    node.warn('api call failed!');\n    return null;\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":420,"wires":[["651e3acca7c91317"]]},{"id":"043652f6aed56a6a","type":"function","z":"f6f2187d.f17ca8","name":"build request","func":"msg.method = \"POST\";\nmsg.url = `${flow.get('mayan.api.url_base')}/cabinets/${msg.mayan.cabinet_id}/documents/add/`;\nmsg.headers = {\n  'Content-Type': 'application/json',\n  'Accept': 'application/json',\n  'Authorization': `Token ${flow.get('mayan.auth.token')}`\n};\nmsg.payload = {\n  'document': `${msg.mayan.document_id}`\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":500,"wires":[["c9240507c578b4ca"]]},{"id":"651e3acca7c91317","type":"function","z":"f6f2187d.f17ca8","name":"mayan data","func":"msg.mayan = {\n    ... msg.mayan,\n    'cabinet_id': 9,\n    'document_id': msg.payload.id\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":500,"wires":[["043652f6aed56a6a"]]},{"id":"c9240507c578b4ca","type":"http request","z":"f6f2187d.f17ca8","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":750,"y":500,"wires":[["a754d9038b0e8dc0"]]},{"id":"08a94d42a1c2eba0","type":"comment","z":"f6f2187d.f17ca8","name":"upload document","info":"","x":220,"y":380,"wires":[]},{"id":"0b93ae2506f22e70","type":"comment","z":"f6f2187d.f17ca8","name":"move to cabinet","info":"","x":220,"y":460,"wires":[]},{"id":"26b16b339747d9c1","type":"function","z":"f6f2187d.f17ca8","name":"mayan data","func":"msg.mayan = {\n    'document_type_id': 4,\n    'document__file_name': msg.file.name,\n    'document__file_buffer': msg.payload\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":420,"wires":[["22a3feb2cf341c76"]]},{"id":"269e7e5d177d6a9f","type":"comment","z":"f6f2187d.f17ca8","name":"add metadata","info":"","x":210,"y":540,"wires":[]},{"id":"be978c727309e89d","type":"comment","z":"f6f2187d.f17ca8","name":"obtain auth token","info":"","x":220,"y":160,"wires":[]},{"id":"a754d9038b0e8dc0","type":"function","z":"f6f2187d.f17ca8","name":"response","func":"if (msg.statusCode > 299) {\n    node.warn('api call failed!');\n    return null;\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":500,"wires":[["34fb9392b3ad3bb0"]]},{"id":"117e43e8c99afd1e","type":"function","z":"f6f2187d.f17ca8","name":"build request","func":"msg.method = \"POST\";\nmsg.url = `${flow.get('mayan.api.url_base')}/documents/${msg.mayan.document_id}/metadata/`;\nmsg.headers = {\n  'Content-Type': 'application/json',\n  'Accept': 'application/json',\n  'Authorization': `Token ${flow.get('mayan.auth.token')}`\n};\nmsg.payload = {\n  'metadata_type_id': `${msg.mayan.metadata_type_id}`,\n  'value': `${msg.mayan.metadata__value}`\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":580,"wires":[["34182535f35e04d4"]]},{"id":"34fb9392b3ad3bb0","type":"function","z":"f6f2187d.f17ca8","name":"mayan data","func":"msg.mayan = {\n    ... msg.mayan,\n    'metadata_type_id': 6,\n    'metadata__value': \"4000\"\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":580,"wires":[["117e43e8c99afd1e"]]},{"id":"34182535f35e04d4","type":"http request","z":"f6f2187d.f17ca8","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":750,"y":580,"wires":[["1586a8f07cda7524"]]},{"id":"1586a8f07cda7524","type":"function","z":"f6f2187d.f17ca8","name":"response","func":"if (msg.statusCode > 299) {\n    node.warn('api call failed!');\n    return null;\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":580,"wires":[["3ccda02042ae74fb"]]},{"id":"3ccda02042ae74fb","type":"debug","z":"f6f2187d.f17ca8","name":"completion","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"'complete'","targetType":"jsonata","statusVal":"","statusType":"auto","x":350,"y":660,"wires":[]},{"id":"dea3a619a0e3ce8c","type":"comment","z":"f6f2187d.f17ca8","name":"http://nodered:1880/ui","info":"","x":240,"y":300,"wires":[]},{"id":"70994f3107e78413","type":"ui_group","name":"Upload File","tab":"6ab69d53c11131b3","order":1,"disp":false,"width":"12","collapse":false,"className":""},{"id":"6ab69d53c11131b3","type":"ui_tab","name":"Document Upload","icon":"dashboard","disabled":false,"hidden":false}]
2 Likes

Thanks for sharing this process. I don’t use nodered, so I can’t download your flow to review.

Can you tell me when you are adding the metadata values, do you need to pull the metadata fields first, or are you hardcoding them into your flow?

Also are you using HTTP PATCH when you do?

Thanks

I am hardcoding the Metadata Type and its value for now; using POST.

image

1 Like

That is very helpful. I’m working doing something very similar with n8n. So this is great timing for me!

Thank you

2 Likes

Interesting, first time I am learning about n8n.

I’ve been using it for a couple of years, it’s been very dependable.

Can you post the full URL with headers, parameters, and body?

When I look at the Mayan API docs, I can’t seem to get it to match what I see here.

Thanks

POST /api/v4/documents/upload/ HTTP/1.1
Host: 127.0.0.1:8080
Authorization: Token 5f10c9406d99209a0065a5da4862b86dced18053
Content-Length: 321
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="document_type_id"

4
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="/C:/Users/chris/OneDrive/Documents/Screenshot_1.png"
Content-Type: image/png

(data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Thank you. If I could ask, could you also show me the url for when you added the metadata?

Thanks

URL: POST http://mayan-app-1:8000/api/v4/documents/72/metadata/

Headers: { "Content-Type":"application/json", "Accept":"application/json", "Authorization":"Token 5f10c9406d99209a0065a5da4862b86dced18053" }

Payload: {"metadata_type_id":"6","value":"4000"}

Thank you. That’s what I thought it should be. I’ve been having trouble making mine work. I must have a typo or something.