> ## Documentation Index > Fetch the complete documentation index at: https://docs.restaurant365.com/llms.txt > Use this file to discover all available pages before exploring further. # Generate Upload URL > Step 1 of 3. Returns a pre-signed Amazon S3 URL that accepts exactly one file — the size and MIME type declared in this request — together with the generated storage file name needed in step 3. The URL expires 5 minutes after it is issued and should be treated as a secret. **Security** The requesting user must have **all** of the following permissions: - Accounts Payable → AP Invoices → Edit AP Invoices - Accounts Payable → AP Credit Memos → Edit AP Credit Memos - Accounts Payable → AP Payments → Edit AP Payments ## OpenAPI ````json POST /public/v1/accounting/accounts-payable/attachments/upload-url { "openapi": "3.0.1", "info": { "title": "R365 Public API", "contact": { "name": "R365 Services Team", "email": "r365@restaurant365.com" }, "version": "1.0" }, "security": [ { "Bearer": [] } ], "paths": { "/public/v1/accounting/accounts-payable/attachments/upload-url": { "post": { "tags": [ "accounting" ], "summary": "Generate Upload URL", "description": "Step 1 of 3. Returns a pre-signed Amazon S3 URL that accepts exactly one file — the size and MIME type declared in this request — together with the generated storage file name needed in step 3. The URL expires 5 minutes after it is issued and should be treated as a secret.\n\n**Security**\n\nThe requesting user must have **all** of the following permissions:\n\n- Accounts Payable → AP Invoices → Edit AP Invoices\n- Accounts Payable → AP Credit Memos → Edit AP Credit Memos\n- Accounts Payable → AP Payments → Edit AP Payments", "parameters": [ { "$ref": "#/components/parameters/TenantId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAttachmentUploadUrlRequest" } } } }, "responses": { "200": { "description": "A size-constrained pre-signed upload URL and generated storage file name were created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAttachmentUploadUrlResponse" } } } }, "400": { "description": "Bad Request — a parameter value is malformed, of the wrong type, or outside its allowed range." }, "401": { "description": "Unauthorized — the bearer token is missing, invalid or expired, or is not valid for the specified tenant." }, "403": { "description": "Forbidden — the caller lacks the permission required for this operation." }, "404": { "description": "Not Found — the requested resource does not exist, or the supplied continuation token is unknown or has expired." }, "409": { "description": "Conflict — the request conflicts with the current state of the resource and cannot be applied." }, "422": { "description": "Unprocessable Entity — a required parameter is missing, or a supplied parameter value is invalid." }, "429": { "description": "Too Many Requests — the caller has exceeded the rate limit; retry after a short delay." }, "500": { "description": "Internal Server Error — an unexpected error occurred while processing the request." } }, "x-d360-narrative-url": "

Attaching a file to an existing AP transaction takes three calls. Only the first and third are Restaurant365 endpoints; the second is a direct upload to Amazon S3 using a URL the first call returns.

  1. Generate Upload URL — POST /public/v1/accounting/accounts-payable/attachments/upload-url

  2. Upload File to S3 — upload the file from your device to the returned URL

  3. Register Attachment — POST /public/v1/accounting/accounts-payable/attachments

Supported AP transaction types are invoices, credit memos and payments. The transaction must already exist; attaching a file is a separate operation from creating the transaction.

" } } }, "components": { "parameters": { "TenantId": { "name": "x-r365-context-tenant-id", "in": "header", "required": true, "description": "Identifies the R365 tenant the request applies to. Required on every request.", "schema": { "type": "string" } } }, "schemas": { "CreateAttachmentUploadUrlRequest": { "type": "object", "additionalProperties": false, "required": [ "fileName", "fileType", "fileSizeBytes" ], "properties": { "fileName": { "type": "string", "description": "The original file name, including an extension that matches fileType — for example \"receipt-scan.pdf\" with fileType \"application/pdf\". The extensions .jpg, .jpeg and .jpe are interchangeable, as are .tif and .tiff." }, "fileType": { "type": "string", "enum": [ "application/pdf", "image/jpeg", "image/jpg", "image/jpe", "image/png", "image/tiff", "image/tif", "image/heic", "image/bmp", "image/gif", "image/x-jp2", "image/svg+xml", "text/csv", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ], "description": "The MIME content type of the file. The file you upload in step 2 must be of this type." }, "fileSizeBytes": { "type": "integer", "format": "int64", "minimum": 1, "description": "The exact size of the file in bytes. The file you upload in step 2 must be exactly this size. The default maximum is 26,214,400 bytes (25 MiB), configurable per organization." } } }, "CreateAttachmentUploadUrlResponse": { "type": "object", "additionalProperties": false, "properties": { "url": { "type": "string", "description": "The pre-signed Amazon S3 URL to PUT the raw file bytes to. Expires 5 minutes after it is issued. Treat as a secret." }, "newFileName": { "type": "string", "description": "The generated storage file name. Pass this value as newFileName in step 3. It is derived from fileName — special characters are replaced with hyphens and a unique identifier is appended — so it will not match the name you sent." }, "storageType": { "type": "string", "description": "The storage backend the file will be uploaded to. Currently always \"S3\"." } } } }, "securitySchemes": { "Bearer": { "type": "http", "scheme": "Bearer" } } } } ````