PUT <links.upload>
put_links_upload
Step two, to storage rather than to Flow PT, with no Authorization header. It is the only step that moves bytes, and skipping it still lets step three answer 201.
API
Params
| part | value |
|---|---|
| URL | links.upload from step one, verbatim. Presigned, and it expires |
| method | PUT |
| body | the raw bytes |
Authorization |
do not send one. The signature covers the request; a bearer token is not part of it |
Sample requests
import requests
put = requests.put(links["upload"], data=open("clip.mov", "rb"))
print(put.status_code, put.headers.get("ETag"))
Empty body. The headers are the receipt:
200 Content-Length: 0 ETag: "9ef430cc6d563983f362487a051169cd"
The ETag is the md5 of what was sent, so it verifies the transfer without a second call:
import hashlib
assert put.headers["ETag"].strip('"') == hashlib.md5(open("clip.mov", "rb").read()).hexdigest()
Response codes
| status | when |
|---|---|
| 200 | stored |
| 403 | the signature has expired |
Edge cases
- Skipping this step is not detected. Going straight from step one to step three answers 201 and
creates a real Attachment row over an object that was never written.
file_sizereadsnullfor that row and for a good one, so it cannot tell them apart: only fetching the stored file proves it exists. - The URL is presigned and short-lived. Mint it, use it, and never persist it.
- This is the only step that does not go to Flow PT at all.