The new square api versions 42+ have breaking changes. Im trying to upgrade to ver v42, and I am testing in a local dev environment.
I keep getting the following error:
*** square.core.api_error.ApiError: status_code: 400, body: {'errors': [{'category': 'INVALID_REQUEST_ERROR', 'code': 'INVALID_CONTENT_TYPE', 'detail': 'Received multiple request parts. Please only supply zero or one `parts` of type application/json.'}]}
when I try to upload an approx ~800 byte jpeg [very grainy] in the development sandbox for Square Invoice API using the following code:
pdf_filepath = 'local/path/to/file.jpg'
idem_key = 'some-unique_key_like_square_invoice_id'
f_stream = open(pdf_filepath, "rb")
try:
# I have tried using a stream as well, still the same error
invoice_pdf = SQUARE_CLIENT.invoices.create_invoice_attachment(
invoice_id=square_original_invoice.id,
# this also does not work
# image_file=f_stream,
image_file=pdf_filepath,
request={
"description": f"Invoice-{pdf_filepath}",
"idempotency_key": idem_key,
},
)
except ApiError as e:
print(f"ERROR _attach_pdf_to_vendor_payment with errors {e}")
In the online sandbox API, I get the 400 Response error:
// cache-control: no-cache
// content-type: application/json
// date: Wed, 30 Apr 2025 13:35:06 GMT
// square-version: 2025-04-16
{
"errors": [
{
"code": "BAD_REQUEST",
"detail": "Total size of all attachments exceeds Sandbox limit: 1000 bytes",
"category": "INVALID_REQUEST_ERROR"
}
]
}
Once I got a 900 byte jpg to upload in the API explorer (988 byte did not pass), but the SDK still errors using the same file.
Here is a successful API request upload VIA the API Explorer:
content-length: 1267
content-type: multipart/form-data; boundary=----WebKitFormBoundaryUUID38
square-version: 2025-04-16
user-agent: SquareExplorerGateway/1.0 SquareProperty ApiExplorer
------WebKitFormBoundaryUUID38
Content-Disposition: form-data;name="request"
{
"idempotency_key": "UUID-123-456-7869"
}
------WebKitFormBoundaryUUID38
Content-Disposition: form-data;name="file";filename="900b.jpeg"
Content-Type: image/jpeg
����
Here is the unsuccessful API request via my django server, using the same file:
content-length: 568
content-type: multipart/form-data; boundary=djangoUUID
square-version: 2025-04-16
accept-encoding: gzip
accept: */*
user-agent: squareup/42.0.0.20250416
--djangoUUID
Content-Disposition: form-data;name="request"
Content-Type: application/json;charset=utf-8
{
"description": "Invoice-path/to/file/900b.jpeg",
"idempotency_key": "path/to/original/file/normal-invoice.pdf"
}
--djangoUUID
Content-Disposition: form-data;name="image_file"
Content-Type: image/jpeg
/path/to/file/900b.jpeg
--djangoUUID--
Note the successful request :
Content-Disposition: form-data;name="file";filename="900b.jpeg"
Content-Type: image/jpeg
and the unsuccessful request:
Content-Disposition: form-data;name="request"
Content-Type: application/json;charset=utf-8
Content-Disposition: form-data;name="image_file"
Content-Type: image/jpeg`
specifically:
name="image_file"
vs name="file"
and application/json;charset=utf-8
The headers for the unsuccessful API call are:
{
"date": "Sat, 03 May 2025 22:47:34 GMT",
"content-type": "application/json",
"transfer-encoding": "chunked",
"connection": "keep-alive",
"cf-ray": "ab-…-WER",
"cf-cache-status": "DYNAMIC",
"cache-control": "no-cache",
"strict-transport-security": "max-age=631152000; includeSubDomains; preload",
"x-envoy-decorator-operation": "/v2/invoices/**",
"x-request-id": "58-…-80",
"x-sq-dc": "aws",
"x-sq-istio-migration-ingress-proxy": "sq-envoy",
"x-sq-istio-migration-ingress-region": "us-west-2",
"x-sq-region": "us-west-2",
"vary": "Accept-Encoding",
"server": "cloudflare"
}
The headers from the browser network inspector:
:authority explorer-gateway.squareup.com
:method POST
:path /v2/invoices/inv:0-Ch…wI/attachments
:scheme https
accept application/json
accept-encoding. gzip, deflate, br, zstd
accept-language. en-US,en;q=0.9
authorization Bearer AE…ju
cache-control no-cache
content-length 1250
content-type. multipart/form-data; boundary=----WebKitFormBoundaryCP3GAXwMvwBUTlwU
origin https://developer.squareup.com
pragma no-cache
priority u=1, i
referer https://developer.squareup.com/
sandbox-mode true
sec-ch-ua "Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"
sec-ch-ua-mobile ?0
sec-ch-ua-platform "macOS"
sec-fetch-dest empty
sec-fetch-mode cors
sec-fetch-site same-site
square-version 2025-04-16
user-agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
x-square-property. ApiExplorer
Notes:
To try and send zero parts
, if I do an API call without a request
parameter:
invoice_pdf = SQUARE_CLIENT.invoices.create_invoice_attachment(
invoice_id=self.square_original_invoice.id,
image_file=pdf_filepath,
)
I get a response of INVALID_REQUEST_ERROR BAD_REQUEST Bad request.
If I do an API call with an empty request
parameter:
invoice_pdf = SQUARE_CLIENT.invoices.create_invoice_attachment(
invoice_id=self.square_original_invoice.id,
image_file=pdf_filepath,
request={},
)
I get the same error INVALID_REQUEST_ERROR INVALID_CONTENT_TYPE Received multiple request parts. Please only supply zero or one
parts of type application/json.
I do remember the parameter name (file
vs image_file
) image being an issue with outdated docs, but the current docs show the newer parameter of image_file
being correct
Related Docs https://developer.squareup.com/docs/invoices-api/attachments
This worked fine in the old pre 42 API, with minor syntax change, and I know the limit is now imposed to have a 1000 byte limit for the attachment, but why can't I upload attachments in the sandbox now?
Square's internal developer forum link : https://developer.squareup.com/forums/t/new-api-for-invoice-attachments-error-received-multiple-request-parts-please-only-supply-zero-or-one-parts-of-type-application-json/22126
Docs should read something like this:
Uploads a file and attaches it to an invoice. This endpoint accepts HTTP multipart/form-data file uploads with a JSON request
part and a image_file
part. The image_file
part must INCLUDE a readable stream
in the form of a file (or bytes) [supported formats: GIF, JPEG, PNG, TIFF, BMP, or PDF.], and optionally filename, content_type, headers. See core.File for additional details related to the image_file
Final working code:
f_stream = open(attachment_filepath, "rb")
mime_type, encoding = mimetypes.guess_type(attachment_filepath)
invoice_pdf = SQUARE_CLIENT.invoices.create_invoice_attachment(
invoice_id=self.square_original_invoice.id,
image_file=(
attachment_filepath,
attachment_filepath, #can also be f_stream
mime_type,
),
request={
"idempotency_key": idem_key,
"description": f"Invoice-{attachment_filepath}",
},
)