Public URL for invoice not available via API?

Hey everyone, I am trying to use the API to generate a custom invoice, publish said invoice, and then I plan to share this invoice manually using automation outside of Square to my customer. And I need to be able to capture the public URL that I will share to the user and be able to put it into my email. I look at the API documentation and it says that the URL should be generated, but all of my tests within the Explorer itself, as well as outside of it, has not returned the URL. What am I doing wrong? Could someone help me? Thanks.

: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

API Explorer
Get Started
Square Sandbox

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.

To generate and capture the public URL for an invoice that you plan to share manually, you need to ensure that you are setting the delivery_method field to SHARE_MANUALLY when you create the invoice using the CreateInvoice endpoint. This setting instructs Square not to send the invoice to the customer automatically, and the URL will be included in the public_url field of the PublishInvoice response.

Here are the steps you should follow:

  1. Create the Invoice: Use the CreateInvoice endpoint to create a draft invoice. Make sure to set the delivery_method to SHARE_MANUALLY.

  2. Publish the Invoice: After creating the draft invoice, use the PublishInvoice endpoint to publish it. Once the invoice is published, Square generates the invoice payment page and includes the public_url in the response.

  3. Capture the URL: The public_url field in the PublishInvoice response contains the link to the Square-hosted invoice payment page. You can then use this URL in your email or any other communication method to share it with your customer.

If you are not seeing the public_url in your response, double-check that you are correctly setting the delivery_method to SHARE_MANUALLY and that you are using the PublishInvoice endpoint after creating the draft invoice. If these steps are followed correctly, the URL should be generated and returned in the response.

This answer was reviewed by @Bryan-Square.

Hey, thanks for shooting that over. So I’ve gone through and done that, and here’s the structure that I’m using in my call. And here’s the output. Now, what’s interesting is sometimes it shows the public URL and other times it does not. I’m going through the process of creating the order, creating the invoice, then generating a share URL with the following structure. It returns all data except the public share URL, which is the only thing I need. And I’m still confused as to why. I followed what you’d mentioned.

/v2/invoices/{{97.body.invoice.id}}/publish

Standard Headers

{
“idempotency_key”: “{{uuid}}”,
“version”: 1
}

Returns something like:

[
{
“body”: {
“invoice”: {
“id”: “inv:0-ChAnP_XM0l9UPCjSKKXBY02oENgO”,
“version”: 3,
“location_id”: “L3ZTJHV239CNR”,
“order_id”: “K27kBdVB16mSrnbcqQgw8ir1ulLZY”,
“payment_requests”: [
{
“uid”: “62015eb9-615c-4599-b00a-cfbba5339376”,
“request_type”: “DEPOSIT”,
“due_date”: “2025-10-09”,
“percentage_requested”: “50”,
“tipping_enabled”: false,
“computed_amount_money”: {
“amount”: 43003,
“currency”: “USD”
},
“total_completed_amount_money”: {
“amount”: 0,
“currency”: “USD”
},
“automatic_payment_source”: “NONE”
},
{
“uid”: “454eed54-4a33-4241-983c-83aea13bb6f1”,
“request_type”: “BALANCE”,
“due_date”: “2025-10-10”,
“tipping_enabled”: false,
“computed_amount_money”: {
“amount”: 43003,
“currency”: “USD”
},
“total_completed_amount_money”: {
“amount”: 0,
“currency”: “USD”
},
“automatic_payment_source”: “NONE”
}
],
“primary_recipient”: {
“customer_id”: “XW4STJY6684YGS8480CVBKB0Z4”,
“given_name”: “mARRY”,
“family_name”: “MARGATAYTE”,
“email_address”: “[email protected]”,
“phone_number”: “5133019101”,
“company_name”: “SDFGSDFG”
},
“invoice_number”: “INV-2025-10-11-K27kBdVB16mSrnbcqQgw8ir1ulLZY”,
“title”: “Stellar Skate Spectacular”,
“description”: “KIDS and TEEN Bday\nEvent Manager: Kaek Smoak\nCredit card payments subject to 3% fee.\nAll cancellation notices must be in writing, 7 days in advance for refund.\nCurrent total for food and beverage = $345”,
“scheduled_at”: “2025-10-09T23:16:59Z”,
“status”: “SCHEDULED”,
“timezone”: “America/New_York”,
“created_at”: “2025-10-09T18:17:01Z”,
“updated_at”: “2025-10-09T18:24:46Z”,
“accepted_payment_methods”: {
“card”: true,
“square_gift_card”: false,
“bank_account”: true,
“buy_now_pay_later”: false,
“cash_app_pay”: false
},
“delivery_method”: “SHARE_MANUALLY”,
“sale_or_service_date”: “2025-10-11”,
“store_payment_method_enabled”: false
}
},
“headers”: {
,
“server”: “cloudflare”
},
“statusCode”: 200
}
]

Why would it return this, but still not simply return the public share URL as well?

I just checked that invoice and I see the link in version 4 of the invoice. Was version 3 of the invoice what you got back from the API? :slight_smile:

For some reason, even using your developer portal, I’m doing this multiple times. I’ve wasted 12 hours on this now, and it still does not work consistently. I will get it to work once, and then it stops working. Can I just get on the call with one of you guys to get this solved? Because we’re waiting on getting this fixed, and every time we try to get it to work, it works once, and then it fails on everything else, even though we’re passing through the versioning.

I just don’t understand why something so simple just isn’t occurring.

Square CURL Dev Portal Example:

curl https://connect.squareup.com/v2/invoices/inv%3A0-ChCOBu-Q_2-ml9Q1tiJPbU4EENgO/publish \ -X POST \ -H ‘Square-Version: 2025-09-24’ \ -H ‘Authorization: Bearer PUBLIC KEY’ \ -H ‘Content-Type: application/json’ \ -d ‘{ “idempotency_key”: “b5541137-2b34-4885-876b-5d58d1e893cb”, “version”: 1 }’

Output:

// cache-control: max-age=0, private, must-revalidate // content-encoding: gzip // content-type: application/json // date: Mon, 13 Oct 2025 20:50:50 GMT // square-version: 2025-09-24 { “invoice”: { “id”: “inv:0-ChCOBu-Q_2-ml9Q1tiJPbU4EENgO”, “version”: 2, “location_id”: “L3ZTJHV239CNR”, “order_id”: “C2FisOqZ25f44a3Q6yy5lM2IwR6YY”, “payment_requests”: [ { “uid”: “5d137dc7-e737-4299-8570-523706e4535d”, “request_type”: “DEPOSIT”, “due_date”: “2025-10-13”, “percentage_requested”: “50”, “tipping_enabled”: false, “computed_amount_money”: { “amount”: 102153, “currency”: “USD” }, “total_completed_amount_money”: { “amount”: 0, “currency”: “USD” }, “automatic_payment_source”: “NONE” }, { “uid”: “eff1dad4-8d3c-4646-b838-4a366103a379”, “request_type”: “BALANCE”, “due_date”: “2025-10-17”, “tipping_enabled”: false, “computed_amount_money”: { “amount”: 102153, “currency”: “USD” }, “total_completed_amount_money”: { “amount”: 0, “currency”: “USD” }, “automatic_payment_source”: “NONE” } ], “primary_recipient”: { “customer_id”: “DKBYAVFV86ZMVQEXDWZ3ANSW00”, “given_name”: “Example”, “family_name”: “Test”, “email_address”: “[email protected]”, “phone_number”: “1555123456”, “company_name”: “Ad Labs Marketing” }, “invoice_number”: “INV-2025-10-18-C2FisOqZ25f44a3Q6yy5lM2IwR6YY”, “title”: “Stellar Skate Spectacular”, “description”: “Adult Bday or Group Booking (21+)\nEvent Manager: Kaek Smoak\nCredit card payments subject to 3% fee.\nAll cancellation notices must be in writing, 7 days in advance for refund.\nCurrent total for food and beverage = $362”, “scheduled_at”: “2025-10-13T21:49:18Z”, “status”: “SCHEDULED”, “timezone”: “America/New_York”, “created_at”: “2025-10-13T20:49:19Z”, “updated_at”: “2025-10-13T20:50:50Z”, “accepted_payment_methods”: { “card”: true, “square_gift_card”: false, “bank_account”: true, “buy_now_pay_later”: false, “cash_app_pay”: false }, “delivery_method”: “SHARE_MANUALLY”, “sale_or_service_date”: “2025-10-18”, “store_payment_method_enabled”: false } }

Still does not have the Public URL which we require, but has everything else we dont need at this point.

Based on everything you guys have told me, if I went to go rerun this exact invoice, I would now use versioning two, and I would use the exact endpoint that is above with our authorization bearer key with that same date of most recent update, and then the return output should have a body with the URL attached. However, if I do that, it’s not showing me what I need.

Do you have a retry that will continue to call GetInvoice till the URL is in the response? :slight_smile: