Developer API
Build on top of your customers' Countova workspaces. Read and write any record over a clean REST API, call server methods, and receive webhooks, all with the same data model you see in the app.
Base URL & isolation
Every Countova workspace is a fully isolated tenant with its own database and its own API, served under its own subdomain:
A credential issued on one workspace only ever reaches that workspace. There is no shared data plane, so building for one customer can never expose another's data. Responses are JSON. All timestamps are ISO 8601.
Authentication
Two ways to authenticate, depending on whether you are building for a single known customer or publishing an app that many customers install.
API keys (token auth)
Best when you build for a specific customer. In their workspace, open Settings → My Settings → API Access → Generate Keys to get an api_key and api_secret. Send them on every request:
# List the first 3 customers
curl https://acme.countova.com/api/resource/Customer?limit_page_length=3 \
-H "Authorization: token API_KEY:API_SECRET"
api@yourapp.com) with only the roles it needs, then generate keys for that user. That keeps your access scoped and easy to revoke.OAuth2 (for published apps)
Best when your app is installed by many customers and you want the customer to grant access with a consent screen. See OAuth2 for partners below.
REST resources
Every record type (DocType) is a resource at /api/resource/<DocType>. URL-encode the DocType name if it contains spaces (for example Sales%20Invoice).
| Action | Request |
|---|---|
| GET List | /api/resource/Item |
| GET One | /api/resource/Item/ITEM-0001 |
| POST Create | /api/resource/Item |
| PUT Update | /api/resource/Item/ITEM-0001 |
| DELETE Delete | /api/resource/Item/ITEM-0001 |
List queries accept fields, filters, limit_page_length, limit_start and order_by:
# Overdue sales invoices, newest first
curl -G https://acme.countova.com/api/resource/Sales%20Invoice \
-H "Authorization: token API_KEY:API_SECRET" \
--data-urlencode 'fields=["name","customer","grand_total","due_date"]' \
--data-urlencode 'filters=[["status","=","Overdue"]]' \
--data-urlencode 'order_by=due_date asc'
# Create a customer
curl -X POST https://acme.countova.com/api/resource/Customer \
-H "Authorization: token API_KEY:API_SECRET" \
-H "Content-Type: application/json" \
-d '{"customer_name":"Harbour Cafe","customer_group":"Commercial","territory":"New Zealand"}'
Response 200 returns the created record:
# Response
{
"data": {
"name": "Harbour Cafe",
"customer_name": "Harbour Cafe",
"customer_type": "Company",
"customer_group": "Commercial",
"territory": "New Zealand",
"creation": "2026-08-26 22:01:52",
"docstatus": 0
}
}
Method calls
Server methods that are whitelisted are callable at /api/method/<dotted.path>. Use these for actions rather than raw record edits, for example submitting a document or fetching a computed figure.
# Current live monthly bill for this workspace
curl https://acme.countova.com/api/method/tripster_erp.tripster_billing.doctype.tripster_subscription.tripster_subscription.get_current_bill \
-H "Authorization: token API_KEY:API_SECRET"
Response 200:
# Response
{
"message": {
"client": "Acme Ltd",
"currency": "NZD",
"active_users": 3,
"billable_users": 0,
"modules": ["Accounting", "Inventory / Warehouse", "Buying",
"Selling / B2B", "Payroll & Timesheets"],
"monthly_total": 40.0,
"setup_fee": 0.0
}
}
Webhooks
To receive events instead of polling, a workspace admin creates a Webhook (search "Webhook" in the workspace). Pick a DocType, an event (after insert, on submit, on update, on cancel), and your HTTPS endpoint. Countova POSTs the record to your URL as it happens, signed with a shared secret you set on the webhook.
X-Frappe-Webhook-Signature header (HMAC-SHA256 of the payload using your webhook secret). Reject any request whose signature does not match.OAuth2 for partners
If you publish an app that many Countova customers install, use OAuth2 so each customer authorises your app with a consent screen, and you never handle their password. The customer's workspace is the authorisation server:
| Endpoint | URL |
|---|---|
| Authorize | /api/method/frappe.integrations.oauth2.authorize |
| Token | /api/method/frappe.integrations.oauth2.get_token |
| Revoke | /api/method/frappe.integrations.oauth2.revoke_token |
| OpenID | /api/method/frappe.integrations.oauth2.openid_profile |
The flow is standard authorization-code: redirect the customer to the authorize endpoint with your client_id, redirect_uri, scope and state; the customer approves; you exchange the returned code for an access token at the token endpoint; then call the API with Authorization: Bearer <access_token>.
client_id or discuss integration, email developers@countova.com.Limits & good manners
- Page through large lists with
limit_startandlimit_page_length(default 20). Avoid pulling everything in one call. - Requests are rate limited per workspace. Back off on
429and retry with a delay. - Always send HTTPS. Keep secrets server-side, never in a browser or mobile app.
- Test against a demo workspace first, grab one here.