Reserves storage for a file and returns where to send its bytes, for callers that
would rather not route the file through this API. Use
Upload attachment instead to send the file in one request.
Three steps:
POST the file’s filename, byte_size and checksum here. The response carries
a signed_id, an upload_url and the upload_headers the next step must send.
PUT the file’s bytes to upload_url, carrying every header in upload_headers
unchanged. The bytes go straight to storage and never pass through this API.
checksum is the base64-encoded MD5 digest of the file, which is the form
Content-MD5 takes - not the hex digest. Generate it with
openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a
wrong digest fails at step 2.
A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes
never arrived is rejected rather than left as a broken attachment, and a reservation
that is never used is cleaned up automatically.
The response tells you where to send the bytes. Send them with a PUT, carrying every header
from upload_headers unchanged.
FILE=$HOME/Downloads/report.pdf# 1. Reserve the upload. `wc -c` pads its output on BSD and macOS, so trim it.curl -X POST "https://<subdomain>.neetodesk.com/api/external/v2/attachment_uploads" \ -H "X-Api-Key: <your_api_key>" \ -H "Content-Type: application/json" \ -d "{ \"filename\": \"$(basename "$FILE")\", \"byte_size\": $(wc -c < "$FILE" | tr -d ' '), \"checksum\": \"$(openssl dgst -md5 -binary "$FILE" | base64)\", \"content_type\": \"application/pdf\" }"# 2. Send the bytes to the upload_url from the response. Pass each header from# upload_headers exactly as the response gave it, values included.curl -X PUT "<upload_url>" \ -H "Content-Type: <upload_headers.Content-Type>" \ -H "Content-MD5: <upload_headers.Content-MD5>" \ --data-binary "@$FILE"# 3. Attach it.curl -X POST "https://<subdomain>.neetodesk.com/api/external/v2/tickets" \ -H "X-Api-Key: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "email": "customer@example.com", "subject": "Printer is jammed", "description": "Report attached.", "attachments": ["<signed_id>"] }'
checksum is the base64-encoded MD5 digest, not the hex digest. The storage service
verifies it, so a hex digest fails at step 2 rather than at step 1.
Send the headers from upload_headers rather than composing your own. Their values are
covered by the upload signature, so an altered or missing one is refused. Which headers
appear depends on how the workspace stores files, so read them from the response rather
than hardcoding the set above.
Base64-encoded MD5 digest of the file, which is the form Content-MD5 takes.
Not the hex digest. Generate it with
openssl dgst -md5 -binary report.pdf | base64.
Pass this to attachments on ticket or comment create once the file's bytes have
been sent to upload_url. It is only accepted in the workspace that reserved it,
and attaching it before the bytes arrive is rejected.
Headers the PUT must carry, exactly as given. Send them unchanged - the
signature covers them, so an altered or missing header is refused by the storage
service.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"NeetoDesk Docs","description":"Developer docs for NeetoDesk: the REST API, the CLI and the MCP server.","colors":{"primary":"#10B981","light":"#34D399","dark":"#059669"},"logo":"https://mintcdn.com/neetodesk-apis/fcjWFGChu0_ZhoJ8/assets/logo.svg?fit=max\u0026auto=format\u0026n=fcjWFGChu0_ZhoJ8\u0026q=85\u0026s=314a72c2599a6af037c5b2afffb942c1","favicon":"https://mintcdn.com/neetodesk-apis/t1YrhlPv0gVQ5VAV/assets/favicon.svg?fit=max\u0026auto=format\u0026n=t1YrhlPv0gVQ5VAV\u0026q=85\u0026s=c3ecdef5c4603c7c449f61988b8a9821","api":{"params":{"expanded":"all"},"playground":{"display":"interactive","proxy":true},"examples":{"defaults":"all","languages":["curl","javascript","python"]}},"appearance":{"default":"light","strict":true},"navbar":{"links":[{"href":"https://www.neeto.com/neetodesk/blog","label":"Blog"},{"href":"https://help.neetodesk.com/","label":"Help center"},{"href":"https://neetodesk.com/","label":"NeetoDesk"}]},"footer":{"socials":{"x":"https://x.com/neetohq","linkedin":"https://www.linkedin.com/company/neeto","youtube":"https://www.youtube.com/channel/UCoQ3Kv2JdZ51WZJ3QFFcI9g","slack":"https://neeto.com/community"}},"styling":{"eyebrows":"section","codeblocks":"system","latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"neetodesk-apis","hasAudioNarration":false,"actualSubdomain":"neetodesk-apis","customDomains":["apidocs.neetodesk.com"],"gitSource":{"type":"github","owner":"neetodeskapis","repo":"neeto-desk-api","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6ae48e8fd51aac6e910a7648","collectionId":"9c97eaa5-7999-44d5-968a-c118e3baad60","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ae48e8fd51aac6e910a7648"},"entitlements":{},"buildId":"6ae48f8439bc6f1b8c4fedf3","clientVersion":"0.0.4021","searchFilterCounts":[{"language":"default","tag":"API","count":52},{"language":"default","tag":"Getting started","count":20},{"language":"default","tag":"API v1 (deprecated)","count":17},{"language":"default","tag":"CLI","count":16},{"language":"default","tag":"Commands","count":9},{"language":"default","tag":"MCP","count":8},{"language":"default","tag":"Ticket fields","count":5},{"language":"default","tag":"Reports","count":5},{"language":"default","tag":"Team members","count":5},{"language":"default","tag":"Comments","count":4}]},"assistantConfig":{},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"theme.css","content":":root{--primary-green:#0da874;--white:#ffffff;--light-gray:#eeeeee;--dark-gray:#1f2937;--text-white:rgb(255, 255, 255);--text-light:rgb(238, 238, 238)}#navbar-transition,[data-nav=true]{background-color:var(--primary-green)!important}.nav-logo{filter:brightness(0) invert(1)!important;height:20px!important;width:auto!important;max-width:none!important}#navbar a,#navbar-transition a,.navbar-link,[data-nav=true] a{color:var(--text-white)!important}#navbar a:hover,#navbar-transition a:hover,.navbar-link:hover,[data-nav=true] a:hover{color:var(--text-light)!important}#navbar .text-sm,#navbar .text-xs,#navbar button,#navbar div,#navbar p,#navbar span{color:var(--white)!important}#navbar .bg-gray-300,#navbar .bg-gray-400,#navbar .bg-gray-500,#navbar .bg-gray-600,[data-headlessui-state=open] [id*=headlessui-dialog-panel]{background-color:var(--white)!important}[data-headlessui-state=open] [id*=headlessui-dialog-panel] .dark\\:text-gray-400,[data-headlessui-state=open] [id*=headlessui-dialog-panel] .navbar-link a,[data-headlessui-state=open] [id*=headlessui-dialog-panel] .text-gray-600{color:var(--dark-gray)!important}#search-bar-entry .text-sm,#search-bar-entry .text-xs,#search-bar-entry button,#search-bar-entry div,#search-bar-entry img,#search-bar-entry p,#search-bar-entry span,#search-bar-entry svg,[data-headlessui-state=open] [id*=headlessui-dialog-panel] .dark\\:hover\\:text-gray-300:hover,[data-headlessui-state=open] [id*=headlessui-dialog-panel] .hover\\:text-gray-900:hover,[data-headlessui-state=open] [id*=headlessui-dialog-panel] .navbar-link a:hover{color:var(--primary-green)!important}"}],"page":{"pagePath":"api/attachments/create-upload","slug":"/api/attachments/create-upload","pageMetadata":{"title":"Reserve an attachment upload","openapi":"bundled/attachments.yaml POST /attachment_uploads","description":"Reserves storage for a file and returns where to send its bytes, for callers that\nwould rather not route the file through this API. Use\n[Upload attachment](/api/attachments/create) instead to send the file in one request.\n\nThree steps:\n\n1. `POST` the file's `filename`, `byte_size` and `checksum` here. The response carries\n a `signed_id`, an `upload_url` and the `upload_headers` the next step must send.\n2. `PUT` the file's bytes to `upload_url`, carrying every header in `upload_headers`\n unchanged. The bytes go straight to storage and never pass through this API.\n3. Pass the `signed_id` in `attachments` on\n [Create ticket](/api/tickets/create) or\n [Create comment](/api/comments/create).\n\n`checksum` is the base64-encoded MD5 digest of the file, which is the form\n`Content-MD5` takes - not the hex digest. Generate it with\n`openssl dgst -md5 -binary report.pdf | base64`. The storage service verifies it, so a\nwrong digest fails at step 2.\n\nA reservation holds no file until step 2 completes. Attaching a `signed_id` whose bytes\nnever arrived is rejected rather than left as a broken attachment, and a reservation\nthat is never used is cleaned up automatically.\n","href":"/api/attachments/create-upload"},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n a: \"a\",\n code: \"code\",\n li: \"li\",\n ol: \"ol\",\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsxs(_components.p, {\n children: [\"Reserves storage for a file and returns where to send its bytes, for callers that\\nwould rather not route the file through this API. Use\\n\", _jsx(_components.a, {\n href: \"/api/attachments/create\",\n children: \"Upload attachment\"\n }), \" instead to send the file in one request.\"]\n }), \"\\n\", _jsx(_components.p, {\n children: \"Three steps:\"\n }), \"\\n\", _jsxs(_components.ol, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"POST\"\n }), \" the file’s \", _jsx(_components.code, {\n children: \"filename\"\n }), \", \", _jsx(_components.code, {\n children: \"byte_size\"\n }), \" and \", _jsx(_components.code, {\n children: \"checksum\"\n }), \" here. The response carries\\na \", _jsx(_components.code, {\n children: \"signed_id\"\n }), \", an \", _jsx(_components.code, {\n children: \"upload_url\"\n }), \" and the \", _jsx(_components.code, {\n children: \"upload_headers\"\n }), \" the next step must send.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"PUT\"\n }), \" the file’s bytes to \", _jsx(_components.code, {\n children: \"upload_url\"\n }), \", carrying every header in \", _jsx(_components.code, {\n children: \"upload_headers\"\n }), \"\\nunchanged. The bytes go straight to storage and never pass through this API.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [\"Pass the \", _jsx(_components.code, {\n children: \"signed_id\"\n }), \" in \", _jsx(_components.code, {\n children: \"attachments\"\n }), \" on\\n\", _jsx(_components.a, {\n href: \"/api/tickets/create\",\n children: \"Create ticket\"\n }), \" or\\n\", _jsx(_components.a, {\n href: \"/api/comments/create\",\n children: \"Create comment\"\n }), \".\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"checksum\"\n }), \" is the base64-encoded MD5 digest of the file, which is the form\\n\", _jsx(_components.code, {\n children: \"Content-MD5\"\n }), \" takes - not the hex digest. Generate it with\\n\", _jsx(_components.code, {\n children: \"openssl dgst -md5 -binary report.pdf | base64\"\n }), \". The storage service verifies it, so a\\nwrong digest fails at step 2.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A reservation holds no file until step 2 completes. Attaching a \", _jsx(_components.code, {\n children: \"signed_id\"\n }), \" whose bytes\\nnever arrived is rejected rather than left as a broken attachment, and a reservation\\nthat is never used is cleaned up automatically.\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,..._provideComponents(),...props.components},{CodeBlock,Heading,Info,Note,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Info)_missingMdxReference(`Info`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(Info,{children:[`Replace `,_jsx(_components.code,{children:`{your-subdomain}`}),` with your workspace’s subdomain. `,_jsx(`br`,{}),` Learn how to find your subdomain in `,_jsx(_components.a,{href:`/api/workspace-subdomain`,children:`Workspace subdomain`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`sending-the-file`,children:`Sending the file`}),`\n`,_jsxs(_components.p,{children:[`The response tells you where to send the bytes. Send them with a `,_jsx(_components.code,{children:`PUT`}),`, carrying every header\nfrom `,_jsx(_components.code,{children:`upload_headers`}),` unchanged.`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`30`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`30`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`FILE`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$HOME`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/Downloads/report.pdf`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:\"# 1. Reserve the upload. `wc -c` pads its output on BSD and macOS, so trim it.\"})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"https://\u003csubdomain>.neetodesk.com/api/external/v2/attachment_uploads\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-Api-Key: \u003cyour_api_key>\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"{`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`filename`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`$(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`basename`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$FILE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\")`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`byte_size`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`: $(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`wc`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -c`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u003c`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$FILE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\" `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`|`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:` tr`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ' '),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`checksum`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`$(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`openssl`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` dgst `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`-md5`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -binary`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$FILE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\" `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`|`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:` base64`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`)`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`content_type`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`application/pdf`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:`\\\\\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` }\"`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# 2. Send the bytes to the upload_url from the response. Pass each header from`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# upload_headers exactly as the response gave it, values included.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` PUT`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"\u003cupload_url>\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: \u003cupload_headers.Content-Type>\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-MD5: \u003cupload_headers.Content-MD5>\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --data-binary`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"@`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$FILE`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# 3. Attach it.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"https://\u003csubdomain>.neetodesk.com/api/external/v2/tickets\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-Api-Key: \u003cyour_api_key>\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '{`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"email\": \"customer@example.com\",`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"subject\": \"Printer is jammed\",`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"description\": \"Report attached.\",`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"attachments\": [\"\u003csigned_id>\"]`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` }'`})}),`\n`]})})}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`checksum`}),` is the `,_jsx(_components.strong,{children:`base64-encoded`}),` MD5 digest, not the hex digest. The storage service\nverifies it, so a hex digest fails at step 2 rather than at step 1.`]})}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`Send the headers from `,_jsx(_components.code,{children:`upload_headers`}),` rather than composing your own. Their values are\ncovered by the upload signature, so an altered or missing one is refused. Which headers\nappear depends on how the workspace stores files, so read them from the response rather\nthan hardcoding the set above.`]})})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":"system","config":{},"pageMetadata":{"title":"Reserve an attachment upload","openapi":"bundled/attachments.yaml POST /attachment_uploads","description":"Reserves storage for a file and returns where to send its bytes, for callers that\nwould rather not route the file through this API. Use\n[Upload attachment](/api/attachments/create) instead to send the file in one request.\n\nThree steps:\n\n1. `POST` the file's `filename`, `byte_size` and `checksum` here. The response carries\n a `signed_id`, an `upload_url` and the `upload_headers` the next step must send.\n2. `PUT` the file's bytes to `upload_url`, carrying every header in `upload_headers`\n unchanged. The bytes go straight to storage and never pass through this API.\n3. Pass the `signed_id` in `attachments` on\n [Create ticket](/api/tickets/create) or\n [Create comment](/api/comments/create).\n\n`checksum` is the base64-encoded MD5 digest of the file, which is the form\n`Content-MD5` takes - not the hex digest. Generate it with\n`openssl dgst -md5 -binary report.pdf | base64`. The storage service verifies it, so a\nwrong digest fails at step 2.\n\nA reservation holds no file until step 2 completes. Attaching a `signed_id` whose bytes\nnever arrived is rejected rather than left as a broken attachment, and a reservation\nthat is never used is cleaned up automatically.\n","href":"/api/attachments/create-upload"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Sending the file","slug":"sending-the-file","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":["/_mintlify/custom/neetodesk-apis/5f665fb1f1635bc3/os-detect.js"],"openApiReferenceData":{"operation":{"summary":"Reserve an attachment upload","description":"Reserves storage for a file and returns where to send its bytes, for callers that\nwould rather not route the file through this API. Use\n[Upload attachment](/api/attachments/create) instead to send the file in one request.\n\nThree steps:\n\n1. `POST` the file's `filename`, `byte_size` and `checksum` here. The response carries\n a `signed_id`, an `upload_url` and the `upload_headers` the next step must send.\n2. `PUT` the file's bytes to `upload_url`, carrying every header in `upload_headers`\n unchanged. The bytes go straight to storage and never pass through this API.\n3. Pass the `signed_id` in `attachments` on\n [Create ticket](/api/tickets/create) or\n [Create comment](/api/comments/create).\n\n`checksum` is the base64-encoded MD5 digest of the file, which is the form\n`Content-MD5` takes - not the hex digest. Generate it with\n`openssl dgst -md5 -binary report.pdf | base64`. The storage service verifies it, so a\nwrong digest fails at step 2.\n\nA reservation holds no file until step 2 completes. Attaching a `signed_id` whose bytes\nnever arrived is rejected rather than left as a broken attachment, and a reservation\nthat is never used is cleaned up automatically.\n","parameters":["a814f25a-046a-48b0-80f3-b6d37d64d727"],"requestBody":"06b7ba7d-1874-412a-bb65-cc9950b78855","responses":{"201":"25fe916d-0933-4ffb-8cd5-74130f9e9f25","422":"7d212fda-43aa-4368-8017-9f7294ddcd1f"},"method":"post","type":"path","servers":["eeeb986a-de48-413a-856d-2144a6806735"],"security":[],"path":"/attachment_uploads","baseUrlOptions":["https://{your-subdomain}.neetodesk.com/api/external/v2"],"title":"Reserve an attachment upload","codeSamples":[],"requestExampleLanguages":["bash","javascript","python"],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"a814f25a-046a-48b0-80f3-b6d37d64d727":{"in":"header","name":"X-Api-Key","description":"Use the X-Api-Key header to provide your workspace API key. Refer to [Authentication](/api/authentication) for more information.","required":true,"schema":{"type":"string","default":"your-api-key","isRequired":true,"description":"Use the X-Api-Key header to provide your workspace API key. Refer to [Authentication](/api/authentication) for more information.","uniqueKey":"","typeLabel":"string"}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","byte_size","checksum"],"properties":{"filename":{"type":"string","description":"Name the file should carry in the workspace.","example":"report.pdf","isRequired":true,"uniqueKey":"filename","typeLabel":"string"},"byte_size":{"type":"integer","description":"Size of the file in bytes.","example":20481,"isRequired":true,"uniqueKey":"byte_size","typeLabel":"integer"},"checksum":{"type":"string","description":"Base64-encoded MD5 digest of the file, which is the form `Content-MD5` takes.\nNot the hex digest. Generate it with\n`openssl dgst -md5 -binary report.pdf | base64`.\n","example":"Y2hlY2tzdW1leGFtcGxl","isRequired":true,"uniqueKey":"checksum","typeLabel":"string"},"content_type":{"type":"string","description":"Media type of the file. Inferred from the filename when omitted.","example":"application/pdf","uniqueKey":"content_type","typeLabel":"string"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"filename":"report.pdf","byte_size":20481,"checksum":"Y2hlY2tzdW1leGFtcGxl","content_type":"application/pdf"}}},"exampleType":"generated"}},"exampleType":"generated"},"responses":{"25fe916d-0933-4ffb-8cd5-74130f9e9f25":{"description":"Created - Upload reserved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"signed_id":{"type":"string","description":"Pass this to `attachments` on ticket or comment create once the file's bytes have\nbeen sent to `upload_url`. It is only accepted in the workspace that reserved it,\nand attaching it before the bytes arrive is rejected.\n","example":"eyJfcmFpbHMiOnsiZGF0YSI6IjEyMyJ9fQ==","uniqueKey":"signed_id","typeLabel":"string"},"upload_url":{"type":"string","description":"Send the file's bytes here with an HTTP `PUT`. The URL is short-lived.","example":"https://neeto-desk.s3.amazonaws.com/3ieihu6erxcialumauhj1pqs07hr?X-Amz-Signature=...","uniqueKey":"upload_url","typeLabel":"string"},"upload_headers":{"type":"object","additionalProperties":{"type":"string","uniqueKey":"upload_headers.additionalProperties","typeLabel":"string"},"description":"Headers the `PUT` must carry, exactly as given. Send them unchanged - the\nsignature covers them, so an altered or missing header is refused by the storage\nservice.\n","example":{"Content-Type":"application/pdf","Content-MD5":"Y2hlY2tzdW1leGFtcGxl"},"uniqueKey":"upload_headers","typeLabel":"object"},"filename":{"type":"string","example":"report.pdf","uniqueKey":"filename","typeLabel":"string"},"content_type":{"type":"string","example":"application/pdf","uniqueKey":"content_type","typeLabel":"string"},"byte_size":{"type":"integer","example":20481,"uniqueKey":"byte_size","typeLabel":"integer"},"checksum":{"type":"string","example":"Y2hlY2tzdW1leGFtcGxl","uniqueKey":"checksum","typeLabel":"string"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"signed_id":"eyJfcmFpbHMiOnsiZGF0YSI6IjEyMyJ9fQ==","upload_url":"https://neeto-desk.s3.amazonaws.com/3ieihu6erxcialumauhj1pqs07hr?X-Amz-Signature=...","upload_headers":{"Content-Type":"application/pdf","Content-MD5":"Y2hlY2tzdW1leGFtcGxl"},"filename":"report.pdf","content_type":"application/pdf","byte_size":20481,"checksum":"Y2hlY2tzdW1leGFtcGxl"}}},"exampleType":"generated"}},"exampleType":"generated"},"7d212fda-43aa-4368-8017-9f7294ddcd1f":{"description":"Unprocessable Entity - `filename`, `byte_size` or `checksum` is missing or\nmalformed, or `byte_size` is over the workspace upload limit (50 MB by default).\n","content":{"_mintlify/placeholder":{"schema":{"type":"object","description":""}}}}},"security":{},"servers":{"eeeb986a-de48-413a-856d-2144a6806735":{"description":"NeetoDesk APIs","url":"https://{your-subdomain}.neetodesk.com/api/external/v2","variables":{"your-subdomain":{"default":"spinkart","description":"Replace **spinkart** with your [workspace's subdomain](/api/workspace-subdomain)."}}}}},"originalFileLocation":"bundled/attachments.yaml"},"subdomain":"neetodesk-apis","siteName":"NeetoDesk Docs","title":"Reserve an attachment upload - NeetoDesk Docs","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-15T08:09:39.115Z","originalFileLocation":"api/attachments/create-upload.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=\"light\";if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"light\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use Upload attachment instead to send the file in one request. Three steps: 1. POST the file\u0026#x27;s filename, byte_size and checksum here. The response carries a signed_id, an upload_url and the upload_headers the next step must send. 2. PUT the file\u0026#x27;s bytes to upload_url, carrying every header in upload_headers unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the signed_id in attachments on Create ticket or Create comment. checksum is the base64-encoded MD5 digest of the file, which is the form Content-MD5 takes - not the hex digest. Generate it with openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically.\">\u003cmeta data-mint-head name=\"application-name\" content=\"NeetoDesk Docs\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Reserve an attachment upload - NeetoDesk Docs\">\u003cmeta data-mint-head property=\"og:description\" content=\"Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use Upload attachment instead to send the file in one request. Three steps: 1. POST the file\u0026#x27;s filename, byte_size and checksum here. The response carries a signed_id, an upload_url and the upload_headers the next step must send. 2. PUT the file\u0026#x27;s bytes to upload_url, carrying every header in upload_headers unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the signed_id in attachments on Create ticket or Create comment. checksum is the base64-encoded MD5 digest of the file, which is the form Content-MD5 takes - not the hex digest. Generate it with openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"NeetoDesk Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://apidocs.neetodesk.com/api/attachments/create-upload\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://neetodesk-apis.mintlify.app/_mintlify/api/og/v2?division=Attachments\u0026amp;title=Reserve+an+attachment+upload\u0026amp;description=Reserves+storage+for+a+file+and+returns+where+to+send+its+bytes%2C+for+callers+that%0Awould+rather+not+route+the+file+through+this+API.+Use%0A%5BUpload+attachment%5D%28%2Fapi\u0026amp;theme=e7c3cc61865b577f8422de04\u0026amp;signature=d9072690911c9430ba3a6c94490e4849ec0b945b4b893e7f63719be27a343889\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Reserve an attachment upload - NeetoDesk Docs\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use Upload attachment instead to send the file in one request. Three steps: 1. POST the file\u0026#x27;s filename, byte_size and checksum here. The response carries a signed_id, an upload_url and the upload_headers the next step must send. 2. PUT the file\u0026#x27;s bytes to upload_url, carrying every header in upload_headers unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the signed_id in attachments on Create ticket or Create comment. checksum is the base64-encoded MD5 digest of the file, which is the form Content-MD5 takes - not the hex digest. Generate it with openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://neetodesk-apis.mintlify.app/_mintlify/api/og/v2?division=Attachments\u0026amp;title=Reserve+an+attachment+upload\u0026amp;description=Reserves+storage+for+a+file+and+returns+where+to+send+its+bytes%2C+for+callers+that%0Awould+rather+not+route+the+file+through+this+API.+Use%0A%5BUpload+attachment%5D%28%2Fapi\u0026amp;theme=e7c3cc61865b577f8422de04\u0026amp;signature=d9072690911c9430ba3a6c94490e4849ec0b945b4b893e7f63719be27a343889\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/neetodesk-apis/tq8Gmpoy_-dPL6IW/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"NeetoDesk Docs\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#10B981\">\u003clink data-mint-head rel=\"canonical\" href=\"https://apidocs.neetodesk.com/api/attachments/create-upload\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api/attachments/create-upload.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://apidocs.neetodesk.com/#organization\",\"name\":\"NeetoDesk Docs\",\"url\":\"https://apidocs.neetodesk.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/neetodesk-apis/fcjWFGChu0_ZhoJ8/assets/logo.svg?fit=max\u0026auto=format\u0026n=fcjWFGChu0_ZhoJ8\u0026q=85\u0026s=314a72c2599a6af037c5b2afffb942c1\"}},{\"@type\":\"WebSite\",\"@id\":\"https://apidocs.neetodesk.com#website\",\"name\":\"NeetoDesk Docs\",\"url\":\"https://apidocs.neetodesk.com\",\"publisher\":{\"@id\":\"https://apidocs.neetodesk.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://apidocs.neetodesk.com/api/attachments/create-upload#webpage\",\"url\":\"https://apidocs.neetodesk.com/api/attachments/create-upload\",\"name\":\"Reserve an attachment upload\",\"description\":\"Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use Upload attachment instead to send the file in one request. Three steps: 1. POST the file's filename, byte_size and checksum here. The response carries a signed_id, an upload_url and the upload_headers the next step must send. 2. PUT the file's bytes to upload_url, carrying every header in upload_headers unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the signed_id in attachments on Create ticket or Create comment. checksum is the base64-encoded MD5 digest of the file, which is the form Content-MD5 takes - not the hex digest. Generate it with openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically.\",\"dateModified\":\"2026-09-15T08:09:39.115Z\",\"isPartOf\":{\"@id\":\"https://apidocs.neetodesk.com#website\"},\"breadcrumb\":{\"@id\":\"https://apidocs.neetodesk.com/api/attachments/create-upload#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://apidocs.neetodesk.com/api/attachments/create-upload#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Attachments\",\"item\":\"https://apidocs.neetodesk.com/api/attachments/create\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Reserve an attachment upload\",\"item\":\"https://apidocs.neetodesk.com/api/attachments/create-upload\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://apidocs.neetodesk.com/api/attachments/create-upload#apireference\",\"headline\":\"Reserve an attachment upload\",\"name\":\"Reserve an attachment upload\",\"description\":\"Reserves storage for a file and returns where to send its bytes, for callers that would rather not route the file through this API. Use Upload attachment instead to send the file in one request. Three steps: 1. POST the file's filename, byte_size and checksum here. The response carries a signed_id, an upload_url and the upload_headers the next step must send. 2. PUT the file's bytes to upload_url, carrying every header in upload_headers unchanged. The bytes go straight to storage and never pass through this API. 3. Pass the signed_id in attachments on Create ticket or Create comment. checksum is the base64-encoded MD5 digest of the file, which is the form Content-MD5 takes - not the hex digest. Generate it with openssl dgst -md5 -binary report.pdf | base64. The storage service verifies it, so a wrong digest fails at step 2. A reservation holds no file until step 2 completes. Attaching a signed_id whose bytes never arrived is rejected rather than left as a broken attachment, and a reservation that is never used is cleaned up automatically.\",\"url\":\"https://apidocs.neetodesk.com/api/attachments/create-upload\",\"mainEntityOfPage\":{\"@id\":\"https://apidocs.neetodesk.com/api/attachments/create-upload#webpage\"},\"dateModified\":\"2026-09-15T08:09:39.115Z\",\"publisher\":{\"@id\":\"https://apidocs.neetodesk.com/#organization\"},\"isPartOf\":{\"@id\":\"https://apidocs.neetodesk.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"NeetoDesk Docs\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"NeetoDesk Docs","nav":{"global":null,"tabs":[{"tab":"API","icon":"code","groups":[{"group":"Getting started","icon":"rocket","pages":[{"title":"NeetoDesk API","href":"/api/introduction"},{"title":"Workspace subdomain","href":"/api/workspace-subdomain"},{"title":"Authentication","href":"/api/authentication"},{"title":"Pagination","href":"/api/pagination"},{"title":"HTTP response codes","href":"/api/response-codes"},{"title":"Getting the ticket ID","href":"/api/getting-ticket-id"}]},{"group":"Tickets","icon":"code","pages":[{"title":"List tickets","openapi":"bundled/tickets.yaml GET /tickets","href":"/api/tickets/list"},{"title":"Create ticket","openapi":"bundled/tickets.yaml POST /tickets","href":"/api/tickets/create"},{"title":"Get ticket","openapi":"bundled/tickets.yaml GET /tickets/{ticket_id}","href":"/api/tickets/get"},{"title":"Update ticket","openapi":"bundled/tickets.yaml PUT /tickets/{ticket_id}","href":"/api/tickets/update"}]},{"group":"Comments","icon":"code","pages":[{"title":"List comments","openapi":"bundled/comments.yaml GET /tickets/{ticket_id}/comments","href":"/api/comments/list"},{"title":"Get a comment","openapi":"bundled/comments.yaml GET /tickets/{ticket_id}/comments/{comment_id}","href":"/api/comments/get"},{"title":"Create comment","openapi":"bundled/comments.yaml POST /tickets/{ticket_id}/comments","href":"/api/comments/create"},{"title":"Create draft comment","openapi":"bundled/comments.yaml POST /tickets/{ticket_id}/drafts","href":"/api/comments/create-draft"}]},{"group":"Attachments","icon":"paperclip","pages":[{"title":"Upload attachment","openapi":"bundled/attachments.yaml POST /attachments","description":"Uploads a file and returns a `signed_id` for it. Pass that id in `attachments` on\n[Create ticket](/api/tickets/create) or\n[Create comment](/api/comments/create) to attach the file.\n\nThe upload is a two-step flow because a file has no home until the record it belongs\nto exists. An uploaded file that is never attached is cleaned up automatically.\n\nA `signed_id` can only be attached in the workspace that uploaded it.\n\nTwo different size limits apply, so it is worth knowing both. This endpoint accepts\na file up to the workspace upload limit, 50 MB by default. Attaching it to a ticket\nor comment applies the smaller attachment limit, 10 MB by default, and a file over\nthat is rejected at attach time rather than silently dropped.\n","href":"/api/attachments/create"},{"title":"Reserve an attachment upload","openapi":"bundled/attachments.yaml POST /attachment_uploads","href":"/api/attachments/create-upload"}]},{"group":"Customers","icon":"code","pages":[{"title":"List customers","openapi":"bundled/customers.yaml GET /customers","description":"Lists the customers in the workspace, oldest first.","href":"/api/customers/list"},{"title":"Get customer","openapi":"bundled/customers.yaml GET /customers/{customer_id}","href":"/api/customers/get"},{"title":"Create customer","openapi":"bundled/customers.yaml POST /customers","href":"/api/customers/create"}]},{"group":"Ticket fields","icon":"list-check","pages":[{"title":"List ticket fields","openapi":"bundled/fields.yaml GET /fields","href":"/api/fields/list"},{"title":"Get ticket field","openapi":"bundled/fields.yaml GET /fields/{field_id}","href":"/api/fields/get"},{"title":"Create ticket field","openapi":"bundled/fields.yaml POST /fields","href":"/api/fields/create"},{"title":"Update ticket field","openapi":"bundled/fields.yaml PATCH /fields/{field_id}","href":"/api/fields/update"},{"title":"Delete ticket field","openapi":"bundled/fields.yaml DELETE /fields/{field_id}","href":"/api/fields/delete"}]},{"group":"Forms","icon":"clipboard-list","pages":[{"title":"List forms","openapi":"bundled/forms.yaml GET /forms","href":"/api/forms/list"}]},{"group":"Reports","icon":"chart-line","pages":[{"title":"Get ticket volume report","openapi":"bundled/reports.yaml GET /reports/tickets","href":"/api/reports/get-ticket-volume"},{"title":"Get ticket volume time series","openapi":"bundled/reports.yaml GET /reports/ticket-time-series","href":"/api/reports/get-ticket-time-series"},{"title":"Get agent performance report","openapi":"bundled/reports.yaml GET /reports/agents","href":"/api/reports/get-agent-performance"},{"title":"Get team performance report","openapi":"bundled/reports.yaml GET /reports/groups","href":"/api/reports/get-team-performance"},{"title":"Get customer satisfaction report","openapi":"bundled/reports.yaml GET /reports/surveys","href":"/api/reports/get-customer-satisfaction"}]},{"group":"Team members","icon":"user-group","pages":[{"title":"List team members","openapi":"bundled/team-members.yaml GET /team-members","href":"/api/team-members/list"},{"title":"Get team member details","openapi":"bundled/team-members.yaml GET /team-members/{team_member_id}","href":"/api/team-members/get"},{"title":"Add team members","openapi":"bundled/team-members.yaml POST /team-members","href":"/api/team-members/add"},{"title":"Update team member","openapi":"bundled/team-members.yaml PATCH /team-members/{team_member_id}","href":"/api/team-members/update"},{"title":"Remove a team member","openapi":"bundled/team-members.yaml DELETE /team-members/{team_member_id}","href":"/api/team-members/remove"}]},{"group":"API v1 (deprecated)","icon":"triangle-exclamation","pages":[{"title":"List tickets","openapi":"bundled-v1/tickets.yaml GET /tickets","href":"/api-reference-v1/tickets/list"},{"title":"Create ticket","openapi":"bundled-v1/tickets.yaml POST /tickets","href":"/api-reference-v1/tickets/create"},{"title":"Get ticket","openapi":"bundled-v1/tickets.yaml GET /tickets/{ticket_id}","href":"/api-reference-v1/tickets/get"},{"title":"Update ticket","openapi":"bundled-v1/tickets.yaml PUT /tickets/{ticket_id}","href":"/api-reference-v1/tickets/update"},{"title":"List comments","openapi":"bundled-v1/comments.yaml GET /tickets/{ticket_id}/comments","href":"/api-reference-v1/comments/list"},{"title":"Create comment","openapi":"bundled-v1/comments.yaml POST /comments","href":"/api-reference-v1/comments/create"},{"title":"Create draft comment","openapi":"bundled-v1/comments.yaml POST /tickets/{ticket_id}/drafts","href":"/api-reference-v1/comments/create-draft"},{"title":"Create customer","openapi":"bundled-v1/customers.yaml POST /customers","href":"/api-reference-v1/customers/create"},{"title":"Get ticket volume report","openapi":"bundled-v1/reports.yaml GET /reports/tickets","href":"/api-reference-v1/reports/get-ticket-volume"},{"title":"Get ticket volume time series","openapi":"bundled-v1/reports.yaml GET /reports/tickets/time_series","href":"/api-reference-v1/reports/get-ticket-time-series"},{"title":"Get agent performance report","openapi":"bundled-v1/reports.yaml GET /reports/agents","href":"/api-reference-v1/reports/get-agent-performance"},{"title":"Get team performance report","openapi":"bundled-v1/reports.yaml GET /reports/groups","href":"/api-reference-v1/reports/get-team-performance"},{"title":"Get customer satisfaction report","openapi":"bundled-v1/reports.yaml GET /reports/surveys","href":"/api-reference-v1/reports/get-customer-satisfaction"},{"title":"List team members","openapi":"bundled-v1/team-members.yaml GET /team_members","href":"/api-reference-v1/team-members/list"},{"title":"Add team members","openapi":"bundled-v1/team-members.yaml POST /team_members","href":"/api-reference-v1/team-members/add"},{"title":"Update team member","openapi":"bundled-v1/team-members.yaml PATCH /team_members/{team_member_id}","href":"/api-reference-v1/team-members/update"},{"title":"Remove a team member","openapi":"bundled-v1/team-members.yaml DELETE /team_members/{team_member_id}","href":"/api-reference-v1/team-members/remove"}]}]},{"tab":"CLI","icon":"rectangle-terminal","pages":[{"title":"CLI","href":"/cli/introduction"}]},{"tab":"MCP","icon":"plug","pages":[{"title":"MCP","href":"/mcp/introduction"}]}]},"title":"Reserve an attachment upload - NeetoDesk Docs","buildId":"dpl_EnxDqhjaNc9a8MVa833TGULPd9uJ","deploymentId":"dpl_EnxDqhjaNc9a8MVa833TGULPd9uJ","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-26-lDe7V.js","utils-CWQmgYtH.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-4-Aw3rSD.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-C2zjXYA5.js","snippetPresets-DFmpFA7C.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-FJpQavWq.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-D_GvPF7k.js","shallow-Bs2ZB0NO.js","getApiPlaygroundDisplay-DCcqJJk-.js","CodeSnippet-DzDxawVe.js","CodeSnippets-aUr7fU-7.js","Page-CT9uE8Ip.js","getPastedJson-Y1fKsj_I.js","icon-DI4zTsDr.js","MethodPill-C0nDlmSQ.js","DuplicateIcon-NUXbx2nQ.js","createIcon-BdTHrPAi.js","DynamicMarkdown-CtpgKJwj.js","SectionHeading-B3x35tOt.js","axios-vUo_bRtp.js","Markdown-3OmurJMP.js","clsx-DMKzROTp.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-1ms6tsT-.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-Ke9xIMFx.js"]}