New API for invoice attachments error : Received multiple request parts. Please only supply zero or one `parts` of type application/json

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.'}]}

It does not make sense that the API response focuses on an “application/JSON” type request when the parameters to the API method are supplied are a file path (stream or text path) and request dict as the docs require.

when I try to upload an approx ~800 byte jpeg [very grainy] in the development sandbox for Square Invoice API using the following code:

        try:
            # I have tried using a stream as well, still the same error
            # f_stream = open(pdf_filepath, "rb")
            invoice_pdf = SQUARE_CLIENT.invoices.create_invoice_attachment(
                invoice_id=self.square_original_invoice.id,
                # image_file=f_stream, <- this also does not work
                # file=pdf_filepath, <- also gives error InvoicesClient.create_invoice_attachment() got an unexpected keyword argument 'file'
                image_file=pdf_filepath,
                # pdf_filepath = 'local/path/to/file.jpg'
                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 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"
    }
  ]
}

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?

Stack overflow link : python - Square API for invoice attachments 'Received multiple request parts. Please only supply zero or one `parts` of type application/json.' - Stack Overflow

:waving_hand: I’m Square Dev AI, here to help answer your question!
Based on the information you’ve provided, the following documentation will help answer your question:

Additional Documentation

Invoices API
Upload and Attach Images
Handling Errors

If this documentation helped you find a solution, please check the box indicating that this reply solves the problem.
Otherwise one of our Developer Advocates will provide additional assistance shortly.

A better readable error provided for upload limits should include the size received - ie We received a file with 451241325 bytes (limit 1000 bytes). This would help when one OS says the size is 987 bytes and another OS says its 1033 bytes.

Also, a 1KB attachment limit is too small. 1Mb minimum is more feasible. Most payload requests are over 1KB.

This limit is just a sandbox limit. Production is 25mb. :slight_smile:

Yes I acknowledge the limit is in the sandbox only, but I still like to test I am attaching something, and Square is getting it. That is only a secondary issue, as I amtrying to find out why I can’t upload attachments at all. The main error is concerning:
*** 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.'}]}

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:

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"
and the unsuccessful request:
Content-Disposition: form-data;name="image_file"

specifically:
name="image_file" vs name="file"

This is similar to a documentation error from the previous <42 API docs ie - square api add image with python fails - Stack Overflow

What’s your application ID? :slight_smile:

What’s your email? :slight_smile:


This says not to share creds. Not sure what you all consider credential

The only credential you should never share is your access token. All other credentials are safe to share. :slight_smile:

Thank you for your help :slight_smile:

Sandbox - sandbox-sq0idb-HnkeMYPHqYI8Y2bKLo36qw

side note/PS:
I got 50+ people fired for disclosing a security vulnerability, and ultimately was fired myself. I know I am an edge case, but the documentation here https://developer.squareup.com/docs/build-basics/access-tokens#credential-types

CLEARLY states that Sandbox application ID and Application ID are Credentials. No where here does it say that these are safe to share. Your request feels counter [security] culture, but my need to invoice today outweighs future security issues for a payment processer.

Maybe update the docs that they are safe to share? or provide a link to rotate credentials?

Thank you for your help :slight_smile:

Are the API logs on the right track? The differences between them the reason? Should I modify the request manually?

What do you mean by are the API Logs on the right track? They log all requests for troubleshooting. Are you seeing something off with them? :slight_smile:

Yes

The main issue remains, I can not upload an attachment to an invoice using the new Square API using the python SDK (in a django project), specifically the invocation of the invoices.create_invoice_attachment method. Tracing the calls to the Square API from the API Logs of the Square App (link is https://developer.squareup.com/apps/sq0idp-{{someIDThatDoesntMatchAppID}}/log), there are two logs that are of interest:
1- the API explorer call that finally succeeds with the 900 byte jpg (successful return in the explorer, and viewed in the logs)
2 - the external django app call to the API using the same 900 byte jpg, the same description, and a unique idempotency key, but this time it errors, returning the error INVALID_REQUEST_ERROR INVALID_CONTENT_TYPE Received multiple request parts. Please only supply zero or one partsof type application/json.

trying to do a ‘simple’ diff of the requests of both calls, there are some differences that don’t seem to be accounted for in any setting file, namely the successful call shows:
Content-Disposition: form-data;name="file";filename="900b.jpeg" Content-Type: image/jpeg for the image and JUST Content-Disposition: form-data;name="request" for the request

and the unsuccessful call shows:
two content headers for the request Content-Disposition: form-data;name="request" Content-Type: application/json;charset=utf-8 and for the image it shows Content-Disposition: form-data;name="image_file" Content-Type: image/jpeg but the name parameter is different [“image_file”, not “file”] and does not include a “filename” keyword

test image located here minus spaces https:// imgur. com/a/ py3i2SU

Are the logs on the correct track to show an issue with the API/calls given the error speaks to multiple requests?

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.

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

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}",
    },
)