> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.solvimon.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.solvimon.com/_mcp/server.

# Invoice payment status

Invoices track collection state separately from their [lifecycle status](/platform-guides/invoicing/invoices/invoice-lifecycle). This page covers the `payment_status` field, and the deprecated `paid` boolean it replaces.

## Payment status

Every invoice exposes a `payment_status` field, giving a more precise view of collection state than a simple paid/unpaid flag:

| Status           | Description                                              |
| ---------------- | -------------------------------------------------------- |
| `UNPAID`         | No payment has been received for the invoice.            |
| `PARTIALLY_PAID` | Some, but not all, of the invoiced amount has been paid. |
| `PAID`           | The full invoiced amount has been received.              |
| `OVERPAID`       | More than the invoiced amount has been received.         |

Desk shows this status on the invoice screen. It updates automatically once the customer pays through the invoice's payment link, or it can be set manually as shown below.

![](/_fern-img/b393b99f5e4f0211c9787c9ddbb041cdac329f69c402d8835ddeb3f14aaf61f0.webp)

## Set the status through the API

Settlement that happened outside Solvimon can be recorded on the invoice directly, with [PATCH /v1/invoices/\{id}](https://docs.solvimon.com/api-docs/transaction-api/invoices/patch-invoices-by-resource-id):

### Request

PATCH [https://test.api.solvimon.com/v\{version}/invoices/\{resourceId}](https://test.api.solvimon.com/v\{version}/invoices/\{resourceId})

**`Mark a final invoice as paid`**

```curl Mark a final invoice as paid
curl -X PATCH https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d \
     -H "X-API-KEY: <apiKey>" \
     -H "Content-Type: application/json" \
     -d '{
  "payment_status": "PAID"
}'
```

**`Mark a final invoice as paid`**

```python Mark a final invoice as paid
import requests

url = "https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d"

payload = { "payment_status": "PAID" }
headers = {
    "X-API-KEY": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

**`Mark a final invoice as paid`**

```javascript Mark a final invoice as paid
const url = 'https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d';
const options = {
  method: 'PATCH',
  headers: {'X-API-KEY': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"payment_status":"PAID"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Mark a final invoice as paid`**

```go Mark a final invoice as paid
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d"

	payload := strings.NewReader("{\n  \"payment_status\": \"PAID\"\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("X-API-KEY", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Mark a final invoice as paid`**

```ruby Mark a final invoice as paid
require 'uri'
require 'net/http'

url = URI("https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["X-API-KEY"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"payment_status\": \"PAID\"\n}"

response = http.request(request)
puts response.read_body
```

**`Mark a final invoice as paid`**

```java Mark a final invoice as paid
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d")
  .header("X-API-KEY", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"payment_status\": \"PAID\"\n}")
  .asString();
```

**`Mark a final invoice as paid`**

```php Mark a final invoice as paid
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d', [
  'body' => '{
  "payment_status": "PAID"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-KEY' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

**`Mark a final invoice as paid`**

```csharp Mark a final invoice as paid
using RestSharp;

var client = new RestClient("https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d");
var request = new RestRequest(Method.PATCH);
request.AddHeader("X-API-KEY", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"payment_status\": \"PAID\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Mark a final invoice as paid`**

```swift Mark a final invoice as paid
import Foundation

let headers = [
  "X-API-KEY": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["payment_status": "PAID"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://test.api.solvimon.com/v1/invoices/invo_JwDeeN0vxxHykcCOpL1d")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Response (200)

```json
{
  "object_type": "INVOICE",
  "id": "invo_JwDeeN0vxxHykcCOpL1d",
  "invoice_number": "INV-2026-10-000014",
  "purchase_order_number": "NWR-PO-88123",
  "customer_id": "cust_UwDeeN0vxxHxMTCJeF1K",
  "billing_entity_id": "bile_OwDeeN0vxxHxNHCWNp1c",
  "status": "FINAL",
  "on_hold": false,
  "type": "ONE_OFF",
  "created_at": "2026-10-09T16:22:05Z",
  "updated_at": "2026-11-03T09:12:40Z",
  "invoice_date": "2026-10-09T07:00:00Z",
  "due_date": "2026-11-08T00:00:00Z",
  "billing_currency": "USD",
  "invoice_amount_including_tax": {
    "quantity": "7500.00",
    "currency": "USD"
  },
  "updated_to_final_at": "2026-10-09T16:40:12Z",
  "payment_status": "PAID",
  "paid": true
}
```

The invoice has to be `FINAL`. Only `PAID` and `UNPAID` can be set this way. `PARTIALLY_PAID` and `OVERPAID` are derived from the paid and open amounts, so they are rejected. The field cannot be set while creating an invoice.

Setting `payment_status` moves the deprecated `paid` boolean with it. Sending both, with values that contradict each other, is rejected on `payment_status`.

## Refunds

`open_invoice_amount` and `paid_invoice_amount` account for refunds. A payment pending refund or reversal still counts as paid; a settled refund reduces `paid_invoice_amount` and reopens the corresponding part of `open_invoice_amount`.

> **Note**
>
> Marking an invoice paid by hand sets `payment_status` along with the paid flag and the open amount. All three fields stay consistent with each other.

## Deprecated: the `paid` boolean

Invoices also still expose a `paid` boolean, which only distinguishes paid from unpaid. It continues to work for backwards compatibility, but it can't represent a partial or over-payment. Use `payment_status` instead.

---