Developers
Set your API contextPick a region and space ID once. Every sample fills in your base URL.
Where do I find these?

In Coevera: open your space's API application and choose Show Access. It shows the space ID and the base URL, whose host starts with your region.

From the API: if you know the space ID, the discovery endpoint GET /api/discovery/spaces/{space_id} returns its region and base URLs, without credentials.

Only the region and space ID are kept, in this browser. Never paste a key or password here.

More in the quickstart

Get startedQuickstart: your first API call

Quickstart: your first API call

Create an API application in Coevera, copy its credentials and list your accounts, in REST or GraphQL, with cURL, TypeScript or Python.

Answer

An admin creates an API application in the CRM and copies its user name, password, space ID and service URL. Your code sends the user name and password with HTTP Basic auth to https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}. A GET on /entities/Accounts with first=3 returns the first three accounts as JSON.

From an empty terminal to a first successful call: create an API application, copy its credentials, and list the accounts in your space. It takes about five minutes, in REST or GraphQL.

Before you begin

  • A Coevera space in which you hold the Admin API Access right. Only such a user can open Applications; for everyone else the Obtain API Key button is disabled.
  • A space to test in. Coevera has no self-serve developer sandbox, so use a space without production data: a 14-day free trial gives you a space of your own. Every Coevera plan includes the API; the monthly call limits are in Plans and limits.
  • curl, Node.js 18 or later, or Python 3.9 or later with requests.

Get API credentials

Every integration authenticates as an API application. Each one has its own user name, password and permissions, so you can give an integration only the access it needs and revoke it without breaking the others.

  1. Open the applications list. In the CRM, open Administration from the space menu (the app switcher calls it Admin) and go to Units, Users & Roles › Applications. The button Administration › General › Obtain API Key opens the same place.

  2. Create the application. Click Create New and name it after your integration, for example Northwind ERP sync. Then click Edit Permissions: Read Only Access on Accounts, in the Records tab, is enough for this quickstart.

  3. Copy the access values. Click Show Access. The API Access dialog shows four values, each with a copy button:

    Value Where it goes
    User Name COEVERA_API_KEY. It starts with your space ID.
    Password COEVERA_API_SECRET. It is shown in full only right after the application is created or its credentials are regenerated, so copy it then.
    Space ID The context bar at the top of this page, and every URL.
    Service URL The host to call, in your space's region. Take it from here or from the discovery endpoint; do not build it by hand.

Generate New, in the same dialog, replaces both the user name and the password, and the previous pair stops working at once.

Only API applications of type Basic have Show Access. An application that an AI connector created is of type OAuth and offers View Permissions only.

Set your context

Pick your region and enter your space ID in the context bar at the top of the page: every sample on this site then fills in your base URL. Keep the credentials out of your code and out of every URL; export them as environment variables:

Shell
export COEVERA_API_KEY="<application user name>"
export COEVERA_API_SECRET="<application password>"   # never commit it

The REST API for a space answers at:

Text
https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}

The region is one of us-east, eu-central, ca-central or ap-southeast; see Regions and base URLs.

Make your first request

List the first three accounts in the space, with only the fields you need: first=3 asks for three records and load-only names the fields each one carries. Pick REST or GraphQL, and your language:

COEVERA_BASE="https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}"

curl -sS --fail-with-body -G "$COEVERA_BASE/entities/Accounts" \
  -d first=3 \
  -d load-only=id,name,email1,modified \
  -u "$COEVERA_API_KEY:$COEVERA_API_SECRET"
import { Buffer } from "node:buffer";
import { env } from "node:process";

const base = `https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}`;
const auth = Buffer.from(`${env.COEVERA_API_KEY}:${env.COEVERA_API_SECRET}`).toString("base64");

const params = new URLSearchParams({ first: "3", "load-only": "id,name,email1,modified" });
const res = await fetch(`${base}/entities/Accounts?${params}`, {
  headers: { Authorization: `Basic ${auth}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`HTTP ${res.status} ${body.name}: ${body.message}`);
console.log(JSON.stringify(body, null, 2));
import json
import os
import sys

import requests

BASE = "https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}"
AUTH = (os.environ["COEVERA_API_KEY"], os.environ["COEVERA_API_SECRET"])

response = requests.get(
    f"{BASE}/entities/Accounts",
    params={"first": 3, "load-only": "id,name,email1,modified"},
    auth=AUTH,
    timeout=30,
)
body = response.json()
if not response.ok:
    sys.exit(f"HTTP {response.status_code} {body.get('name')}: {body.get('message')}")
print(json.dumps(body, indent=2))
COEVERA_GRAPHQL="https://{region}.api.coevera.com/api/v100/app/space/{space_id}/graphql/public"

curl -sS --fail-with-body "$COEVERA_GRAPHQL" \
  -u "$COEVERA_API_KEY:$COEVERA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ entities { account { getByCriteria(first: 3) { totalCount pageInfo { hasNextPage endCursor } edges { node { id name email1 modified } } } } } }"}'
import { Buffer } from "node:buffer";
import { env } from "node:process";

const endpoint = `https://{region}.api.coevera.com/api/v100/app/space/{space_id}/graphql/public`;
const auth = Buffer.from(`${env.COEVERA_API_KEY}:${env.COEVERA_API_SECRET}`).toString("base64");

const res = await fetch(endpoint, {
  method: "POST",
  headers: { Authorization: `Basic ${auth}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    query: `{
      entities {
        account {
          getByCriteria(first: 3) {
            totalCount
            pageInfo { hasNextPage endCursor }
            edges { node { id name email1 modified } }
          }
        }
      }
    }`,
  }),
});
const body = await res.json();
// GraphQL reports errors in the body, often with HTTP 200: check both.
if (!res.ok || body.errors) throw new Error(`HTTP ${res.status}: ${JSON.stringify(body.errors ?? body)}`);
console.log(JSON.stringify(body.data, null, 2));
import json
import os
import sys

import requests

ENDPOINT = (
    "https://{region}.api.coevera.com/api/v100/app/space/"
    "{space_id}/graphql/public"
)
AUTH = (os.environ["COEVERA_API_KEY"], os.environ["COEVERA_API_SECRET"])

QUERY = """
{
  entities {
    account {
      getByCriteria(first: 3) {
        totalCount
        pageInfo { hasNextPage endCursor }
        edges { node { id name email1 modified } }
      }
    }
  }
}
"""
response = requests.post(ENDPOINT, json={"query": QUERY}, auth=AUTH, timeout=30)
body = response.json()
# GraphQL reports errors in the body, often with HTTP 200: check both.
if not response.ok or body.get("errors"):
    sys.exit(f"HTTP {response.status_code}: {json.dumps(body.get('errors', body))}")
print(json.dumps(body["data"], indent=2))

The response looks like this; the ids and names in your own space differ:

200 OKRecorded response · 2026-10-14 · docs sandbox, de-identified
{
  "success": true,
  "total": 4,
  "page_info": {
    "start_cursor": "WyIwYTAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDEiXQ==",
    "end_cursor": "WyIwYTAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDMiXQ==",
    "has_previous_page": false,
    "has_next_page": true
  },
  "data": [
    {
      "id": "0a000000-0000-4000-a000-000000000001",
      "modified": "2026-10-14 09:00:00.000000+00:00",
      "email1": "northwind@example.com",
      "name": "Northwind Analytics"
    },
    {
      "id": "0a000000-0000-4000-a000-000000000002",
      "modified": "2026-10-14 09:00:00.740429+00:00",
      "email1": "contoso@example.com",
      "name": "Contoso Freight"
    },
    {
      "id": "0a000000-0000-4000-a000-000000000003",
      "modified": "2026-10-14 09:00:01.285049+00:00",
      "email1": "wideworld@example.com",
      "name": "Wide World Importers"
    }
  ]
}
Show all 30 linesShow less
200 OKRecorded response · 2026-10-14 · docs sandbox, de-identified
{
  "data": {
    "entities": {
      "account": {
        "getByCriteria": {
          "totalCount": 4,
          "pageInfo": {
            "hasNextPage": true,
            "endCursor": "WyIwYTAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDMiXQ=="
          },
          "edges": [
            {
              "node": {
                "id": "0a000000-0000-4000-a000-000000000001",
                "name": "Northwind Analytics",
                "email1": "northwind@example.com",
                "modified": "2026-10-14T09:00:00.000000+00:00"
              }
            },
            {
              "node": {
                "id": "0a000000-0000-4000-a000-000000000002",
                "name": "Contoso Freight",
                "email1": "contoso@example.com",
                "modified": "2026-10-14T09:00:00.740429+00:00"
              }
            },
            {
              "node": {
                "id": "0a000000-0000-4000-a000-000000000003",
                "name": "Wide World Importers",
                "email1": "wideworld@example.com",
                "modified": "2026-10-14T09:00:01.285049+00:00"
              }
            }
          ]
        }
      }
    }
  }
}
Show all 41 linesShow less

A REST list comes back in an envelope: success, total (how many accounts match, not how many came back), page_info with the cursors for the next page, and data with the records. A single record comes back as success and data. In GraphQL the list comes back as totalCount, pageInfo and edges. Continue with pagination to read more than one page, and querying to filter, sort and trim a list. Every entity and its operations are in the REST reference and the GraphQL reference.

Limit

A REST list request returns at most 100 records per page, and 30 when you do not set first. A larger first answers 400 with ERROR_PARAMETER_INVALID. In GraphQL, set first on every list query.

If you get 401

Every authentication failure answers HTTP 401 with ERROR_UNAUTHORIZED, and the reason is in message; the other errors are in errors. This is what a request without credentials gets; in GraphQL the status is the same and the body is shorter:

401 UnauthorizedRecorded response · 2026-10-15 · docs sandbox, de-identified
{
  "code": 401,
  "name": "ERROR_UNAUTHORIZED",
  "message": "Access denied (Unauthorized call.).",
  "http_status": 401,
  "success": false
}
401 UnauthorizedRecorded response · 2026-10-15 · docs sandbox, de-identified
{
  "status": 401,
  "message": "Access denied (Unauthorized call.).",
  "errorcode": null,
  "traceback": null
}

The causes, each with its fix:

  • No credentials. The reason reads Unauthorized call. Check that both environment variables are set in the shell that runs the program.
  • Wrong space. The user name starts with the ID of the space it belongs to, and works only there. If the space in the URL is a different one, the call is refused like a wrong password: the reason reads Invalid Api user credentials, and it counts as a failed sign-in. Use the space ID from Show Access.
  • Credentials regenerated. Someone pressed Generate New, which replaces the user name and the password. Copy both new values.

A wrong region is not a cause

A call to another region's host is forwarded to the region that holds your space and answers normally, with the same credentials, only more slowly. Use the region the discovery endpoint returns.

Next steps

Before you build on it, read authentication for how the credentials are sent and rotated, permissions and scopes for what an application may do, and REST or GraphQL? for which API to use. Troubleshooting has the common failures in one place.

Verified on 2026-10-15 against API 6.5Behaviour-verified

Every sample on this page ran against the docs sandbox and returned the response shown.