Invoices

Base URL: https://api.cornerspot.net

Authentication: requests use a bearer token, for example Authorization: Bearer YOUR_API_KEY.

List invoices

GET /c/api/v1/invoices

Paginated, filterable invoices. Requires invoices_read + the Member Portal module.

Query parameters

FIELDTYPEDESCRIPTION
filter[status]stringComma-separated statuses (draft, open, paid, partially_paid, overdue, void, uncollectible).
filter[account_id]stringComma-separated account ids.
filter[subscription_id]stringComma-separated subscription ids.
filter[due_before]stringOnly invoices due on or before this date.
sortstringOne of invoice_number_seq,status,due_date,issued_at,total,amount_due,created_at,updated_at; - prefix descending.
pageinteger
per_pageinteger

Code samples

curl --request GET \
  --url 'https://api.cornerspot.net/c/api/v1/invoices' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices"
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, headers=headers, method="GET")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices")

request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
200 OKa page of invoices
401 Unauthorizedmissing or invalid API key
402 Payment Requiredthe team's plan doesn't include the Member Portal module
403 Forbiddenthe key lacks the invoices_read scope

Create a draft invoice

POST /c/api/v1/invoices

Creates a draft invoice. Line items reference catalog products (product_id) or are fully custom (description + unit_price). Taxes and discounts may be attached by id (tax_rate_ids, discount_definition_ids) or inline, at the invoice level or per line item. Requires invoices_write.

Request body

FIELDTYPEDESCRIPTION
invoiceobject
invoice.account_idstring · requiredRequired — billed account.
invoice.currency_idstring
invoice.subscription_idstring
invoice.opportunity_idstring
invoice.due_datestring
invoice.notesstring
invoice.payment_termsstring
invoice.tax_rate_ids[]array of stringInvoice-level taxes by id.
invoice.discount_definition_ids[]array of stringInvoice-level discounts by id.
invoice.line_items[]array of object
invoice.line_items[].product_idstringCatalog product; supplies description/unit_price defaults.
invoice.line_items[].descriptionstring
invoice.line_items[].quantitynumber
invoice.line_items[].unit_pricestring
invoice.line_items[].tax_rate_ids[]array of string
invoice.line_items[].discount_definition_ids[]array of string

Code samples

curl --request POST \
  --url 'https://api.cornerspot.net/c/api/v1/invoices' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "invoice": {
    "account_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "currency_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "subscription_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "opportunity_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "due_date": "2026-01-15",
    "notes": "string",
    "payment_terms": "string",
    "tax_rate_ids": [
      "abc123"
    ],
    "discount_definition_ids": [
      "abc123"
    ],
    "line_items": [
      {}
    ]
  }
}'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    "invoice": {
      "account_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "currency_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "subscription_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "opportunity_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "due_date": "2026-01-15",
      "notes": "string",
      "payment_terms": "string",
      "tax_rate_ids": [
        "abc123"
      ],
      "discount_definition_ids": [
        "abc123"
      ],
      "line_items": [
        {}
      ]
    }
  })
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    "invoice": {
      "account_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "currency_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "subscription_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "opportunity_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
      "due_date": "2026-01-15",
      "notes": "string",
      "payment_terms": "string",
      "tax_rate_ids": [
        "abc123"
      ],
      "discount_definition_ids": [
        "abc123"
      ],
      "line_items": [
        {}
      ]
    }
  })
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices"
payload = json.dumps({
  "invoice": {
    "account_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "currency_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "subscription_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "opportunity_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "due_date": "2026-01-15",
    "notes": "string",
    "payment_terms": "string",
    "tax_rate_ids": [
      "abc123"
    ],
    "discount_definition_ids": [
      "abc123"
    ],
    "line_items": [
      {}
    ]
  }
}).encode("utf-8")
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, data=payload, headers=headers, method="POST")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices")

request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = JSON.generate({
  "invoice": {
    "account_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "currency_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "subscription_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "opportunity_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "due_date": "2026-01-15",
    "notes": "string",
    "payment_terms": "string",
    "tax_rate_ids": [
      "abc123"
    ],
    "discount_definition_ids": [
      "abc123"
    ],
    "line_items": [
      {}
    ]
  }
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer YOUR_API_KEY',
    ],
    CURLOPT_POSTFIELDS => json_encode([
    'invoice' => [
        'account_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
        'currency_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
        'subscription_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
        'opportunity_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
        'due_date' => '2026-01-15',
        'notes' => 'string',
        'payment_terms' => 'string',
        'tax_rate_ids' => [
            'abc123',
        ],
        'discount_definition_ids' => [
            'abc123',
        ],
        'line_items' => [
            [],
        ],
    ],
]),
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
201 Createdcreated
422 Unprocessable Contentvalidation errors

Get an invoice

GET /c/api/v1/invoices/{id}

Path parameters

FIELDTYPEDESCRIPTION
idstring · requiredInvoice id.

Code samples

curl --request GET \
  --url 'https://api.cornerspot.net/c/api/v1/invoices/abc123' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices/abc123"
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, headers=headers, method="GET")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices/abc123")

request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices/abc123',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
200 OKthe invoice
404 Not Foundno such invoice in this team

Update a draft invoice

PATCH /c/api/v1/invoices/{id}

Only draft invoices may be updated; updating a finalized invoice returns 422 not_draft.

Path parameters

FIELDTYPEDESCRIPTION
idstring · requiredInvoice id.

Request body

FIELDTYPEDESCRIPTION
invoiceobject
invoice.due_datestring
invoice.notesstring
invoice.line_items[]array of object

Code samples

curl --request PATCH \
  --url 'https://api.cornerspot.net/c/api/v1/invoices/abc123' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "invoice": {
    "due_date": "2026-01-15",
    "notes": "string",
    "line_items": [
      {}
    ]
  }
}'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    "invoice": {
      "due_date": "2026-01-15",
      "notes": "string",
      "line_items": [
        {}
      ]
    }
  })
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    "invoice": {
      "due_date": "2026-01-15",
      "notes": "string",
      "line_items": [
        {}
      ]
    }
  })
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices/abc123"
payload = json.dumps({
  "invoice": {
    "due_date": "2026-01-15",
    "notes": "string",
    "line_items": [
      {}
    ]
  }
}).encode("utf-8")
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, data=payload, headers=headers, method="PATCH")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices/abc123")

request = Net::HTTP::Patch.new(uri)
request["Content-Type"] = "application/json"
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = JSON.generate({
  "invoice": {
    "due_date": "2026-01-15",
    "notes": "string",
    "line_items": [
      {}
    ]
  }
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices/abc123',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer YOUR_API_KEY',
    ],
    CURLOPT_POSTFIELDS => json_encode([
    'invoice' => [
        'due_date' => '2026-01-15',
        'notes' => 'string',
        'line_items' => [
            [],
        ],
    ],
]),
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
200 OKupdated

Trash a draft invoice

DELETE /c/api/v1/invoices/{id}

Only draft invoices may be trashed; a finalized invoice returns 422 not_draft.

Path parameters

FIELDTYPEDESCRIPTION
idstring · requiredInvoice id.

Code samples

curl --request DELETE \
  --url 'https://api.cornerspot.net/c/api/v1/invoices/abc123' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices/abc123"
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, headers=headers, method="DELETE")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices/abc123")

request = Net::HTTP::Delete.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices/abc123',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
204 No Contenttrashed

Finalize a draft invoice (draft → open)

POST /c/api/v1/invoices/{id}/finalize

Issues the invoice. Errors with 422 not_draft unless the invoice is currently a draft.

Path parameters

FIELDTYPEDESCRIPTION
idstring · requiredInvoice id.

Code samples

curl --request POST \
  --url 'https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize"
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, headers=headers, method="POST")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize")

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices/abc123/finalize',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
200 OKfinalized
422 Unprocessable Contentthe invoice is not a draft

Void a finalized invoice

POST /c/api/v1/invoices/{id}/void

Voids an open/partially_paid/overdue invoice. Errors with 422 not_finalized for drafts and paid invoices.

Path parameters

FIELDTYPEDESCRIPTION
idstring · requiredInvoice id.

Code samples

curl --request POST \
  --url 'https://api.cornerspot.net/c/api/v1/invoices/abc123/void' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123/void', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/invoices/abc123/void', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);
import json
import urllib.request

url = "https://api.cornerspot.net/c/api/v1/invoices/abc123/void"
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
}

request = urllib.request.Request(url, headers=headers, method="POST")

with urllib.request.urlopen(request) as response:
    data = json.loads(response.read().decode("utf-8"))
    print(data)
require "json"
require "net/http"

uri = URI("https://api.cornerspot.net/c/api/v1/invoices/abc123/void")

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts JSON.parse(response.body)
<?php

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/invoices/abc123/void',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$data = json_decode($response, true);
print_r($data);

Responses

STATUSDESCRIPTION
200 OKvoided
422 Unprocessable Contentthe invoice cannot be voided in its current status

Was this article helpful?