Environments, hosts and test data
Your integration talks to one host per workspace. This page explains which host to call, how ARC works out the workspace from that host, what a custom domain changes, and how to test safely when ARC has no sandbox. It builds on Authentication and the two first-request walkthroughs, so it does not repeat them.
Other pages in this guide write the workspace host as <arc-host>. It is typically of the form <workspace>.<your-arc-domain>, where <workspace> is your workspace's subdomain label. Your administrator gives you the exact host, so use that value rather than the pattern.
1. Which URL do I call?
Where to find it: your API host is the address of your ARC console — the host you see in the browser when you sign in, for example https://<workspace>.<your-arc-domain>. An administrator can also copy the full Service API address from Settings → API keys in the console, under API key (scripts & programmatic use). To confirm a host, call GET https://<arc-host>/health, which needs no credential and answers {"status":"ok", …}.
Use the same host for every surface of one workspace, and never mix hosts between workspaces. Store the host and the organizationId together as one connection. See Design your connection model.
| Surface | Path on your workspace host | Credential |
|---|---|---|
| GraphQL | POST /graphql | User JWT from login |
| Service REST | The read routes (products, stock, units, payment terms, warehouses, customers, sales orders and quotes), POST /api/service/sales-quotes, POST /api/service/sales-orders and the bulk-import routes under /api/service/products/bulk. See Service REST API. | gm_live_ API key |
| MCP | /mcp, plus /.well-known/oauth-*, /register, /authorize and /token | OAuth 2.1 |
| Health | GET /health (liveness) and GET /health/ready (readiness, returns 503 when the database is down) | None |
These paths are not versioned, so there is no /v1 segment in the URL. /graphql, /mcp and /health sit at the root. The service routes sit under /api, and so do the JWT-protected REST routes, such as files, exports and the event stream. See Files and exports and Events and webhooks.
The health routes show only that the host, TLS and deployment respond. They do not prove that your credential or workspace works. For the first call, see Make your first service API request.
export ARC_API_URL="https://<arc-host>"
curl --fail-with-body "$ARC_API_URL/health"
curl --fail-with-body "$ARC_API_URL/health/ready"
If a path in the table does not answer on your host, ask your Growmax account manager before you work around it. Do not use a shared sign-up or app host, or a raw hosting-provider address, as your API base. Those are platform hosts, and they do not carry a workspace.
2. How the workspace is identified from the host
ARC takes the first DNS label of the host as the workspace's subdomain. The admin console, the buyer portal and the API apply the same rule. In plain terms, the rule works like this:
- It lowercases and trims the host, and removes any scheme, path, query string and port.
HTTPS://Example-Seller.<your-arc-domain>:443/xbecomesexample-seller. - It keeps only the first label.
example-seller.<your-arc-domain>becomesexample-seller, anda.b.<your-arc-domain>becomesa, notb. - It removes a
-portalsuffix from the first label.example-seller-portal.<your-arc-domain>is the buyer portal of the same workspace, and it also becomesexample-seller. - It returns no workspace for an empty value, a numeric (IPv4) address,
localhost, the reserved labelsapp,api,signupandwww, and platform hosts.
ARC then looks up the workspace whose subdomain equals that label. Subdomains are unique across the platform. If no workspace has that subdomain, login fails.
| Host you call | Workspace label |
|---|---|
example-seller.<your-arc-domain> | example-seller |
EXAMPLE-SELLER.<your-arc-domain>:8443 | example-seller |
example-seller-portal.<your-arc-domain> | example-seller |
app.<your-arc-domain> | none, reserved label |
www.<your-arc-domain> | none, reserved label |
203.0.113.10 | none, numeric address |
localhost:4000 | none |
| A platform host | none |
A workspace's subdomain is a setting on the workspace, and an administrator can change it. See Workspace administration. Store organizationId from the login response as your stable key, and treat the host and the subdomain as configuration that can change.
Do not use the host or the subdomain as a permanent tenant key. Store organizationId, and send subdomain only as the login input described in the next section.
3. Hosts that never carry a workspace
Platform hosts, reserved labels and numeric addresses cannot tell ARC which workspace you mean. When you call from one of these, pass subdomain to login.
For the staff login mutation, the subdomain field takes precedence over the host. Send the bare label, exactly as it appears as the workspace's subdomain, such as example-seller. ARC applies the rule from section 2 to the value you send: a full hostname is cut to its first label, a -portal suffix is dropped, and a reserved label is ignored, so the host is used instead.
mutation Login($input: LoginInput!) {
login(loginInput: $input) {
accessToken
refreshToken
expiresIn
tokenType
organizationId
organization {
id
name
subdomain
}
}
}
{
"input": {
"email": "developer@example.com",
"password": "<password>",
"subdomain": "<workspace>"
}
}
login is public, so it needs no permission. The response fields are the same as in Make your first GraphQL request.
A login sent without subdomain to a host that identifies no workspace can match the same email address in more than one workspace, because a user's email is unique per workspace, not across the platform. The login may then sign in the wrong account or fail. Always send subdomain, as the bare label.
A subdomain that matches no workspace fails with the same generic Invalid credentials message as a wrong password. Check the label instead of retrying. Login is rate-limited, and repeated failures can lock an account, so back off between attempts.
If the same email belongs to several workspaces, sign in first and then list them. myOrganizations returns each active workspace where the signed-in email has an active account, with its id, name, subdomain and role. It needs a signed-in user and no permission string.
query MyWorkspaces {
myOrganizations {
id
name
subdomain
role
}
}
To work in another of those workspaces, call login again with that workspace's subdomain, and keep each workspace as its own connection with its own tokens. The admin console's workspace switcher uses switchOrganization for the same purpose. See the API reference.
Buyer-facing and portal operations resolve the workspace differently. Check the operation's description in the API reference before you rely on subdomain there.
4. Custom domains
A custom domain is a hostname your seller owns, added by an administrator in the settings. It points to the buyer portal only. It does not give you an API host.
- Use your workspace host for GraphQL, the service API and MCP, even if your seller also has a custom domain.
- A custom domain serves traffic only when its status is
ACTIVE. The statuses arePENDING,ACTIVE,FAILEDandDISABLED. A new domain starts asPENDINGuntil its DNS validation completes. - Every workspace's portal is also reachable at its platform hostname, even with no custom domain.
organizationDomainslists that as a read-only row of kindPLATFORM_SUBDOMAIN. Itshostnameis the portal hostname, not the API host, so do not take the API host from it. - A hostname belongs to one workspace only. The platform's own domain and reserved or documentation names, such as
example.comand the.testand.localhostendings, are refused. - Removing a custom domain disables it. The hostname stays reserved for that workspace.
- A domain can be made primary only when it is a
CUSTOMdomain with statusACTIVE. - Adding, removing, retrying setup and changing the primary domain are limited to the
ADMINandMANAGERroles.
Reading the list requires any one of settings:organization:read or settings:tenant:read.
query PortalHostnames {
organizationDomains {
id
hostname
kind
status
isPrimary
statusMessage
verifiedAt
}
}
Do not hard-code a custom domain as your API base, and do not derive subdomain from one. A custom domain such as orders.example.com would parse to the label orders, which is not your workspace.
5. There is no sandbox
ARC has no sandbox environment, no test-mode API keys and no test flag on a workspace. Every request acts on the live data of the workspace named by its credential.
- API keys start with
gm_live_, followed by 48 hex characters. A key that does not start withgm_live_is rejected, and there is nogm_test_form. - A
gm_live_key writes to real data. - Some seller-side delivery-carrier and payment-gateway settings have a live or test environment choice. That choice switches the seller's own provider credentials between that provider's live and test endpoints. It is not an ARC sandbox.
- The
environmentvalue in the health response describes the server process. It is not a switch for test mode. - API keys also need a plan that includes API access. See Authentication.
- New workspaces are provisioned by Growmax. Ask your Growmax account manager whether a separate non-production workspace is available for your seller, and on which plan.
Test safely
- Ask first. Ask your administrator or your Growmax account manager whether the seller has a separate non-production workspace. If it does, that workspace has its own subdomain, users and API keys, and a key from one workspace never works in another. Do not assume one exists.
- Start read-only. Prove connectivity with a read-only key and read queries. Use the preview steps that exist. Preview a bulk import before you approve it, see Bulk import. Run
invoiceCancellationPreviewbeforecancelInvoice, see Invoices and payments. There is no general dry-run flag on create operations. - Do not write test data to production unless the seller's administrator has agreed in writing. If you must, mark every record clearly, for example with a
ZZ-TESTprefix in its name or reference. That prefix is a convention for your team, not a platform feature. Expect that you cannot erase these records completely. See the cleanup section below. - Use
@example.comfor test contacts. Test customers and contacts need@example.comaddresses, never a real person's address. ARC screens recipient addresses before it sends mail and will not deliver to reserved endings such as.test,.invalid,.localhost,.localand.example, so do not use those either. Creating a customer with an email contact can also create a buyer-portal login for that address. See Customers and partners. Do not send documents or invites for test records. - Use a least-privilege user or key for every test. See the Production checklist.
- Make idempotency keys unique per run, so a retried test does not collide with an earlier one. See Errors, pagination and idempotency.
6. Clean up test records
Every GraphQL remove in the table below is a soft delete. Deleted rows are hidden from reads but kept. The table shows what you can remove and what you cannot.
| Record | Can you remove it? | Rule and permission |
|---|---|---|
| Quote | Yes, if it is a draft or cancelled. Use removeQuote. | Requires sales:quote:delete. Refused with Only draft or cancelled quotes can be deleted |
| Sales order | Yes, if it is a draft or cancelled and has no linked invoice. Use removeSalesOrder. | Requires sales:sales_order:delete. Refused with Only draft or cancelled sales orders can be deleted or Cannot delete a sales order with linked invoices |
| Sales order past draft | Cancel it first with cancelSalesOrder, then remove it as above. Cancel is refused for a shipped, delivered, returned or already cancelled order, and while the order has an invoice that is not cancelled. An order that was ever invoiced cannot be removed, because a cancelled invoice stays linked to it. | cancelSalesOrder requires sales:sales_order:cancel |
| Invoice | No. deleteInvoice always refuses. Cancel the invoice with cancelInvoice after invoiceCancellationPreview. It stays on record. See Invoices and payments. | cancelInvoice requires payments:invoice:cancel |
| Payment | No. Payments are not deleted. Void one with voidPayment. See Invoices and payments. | voidPayment requires payments:payment:void |
| Product | Yes, with removeProduct. The soft delete also removes its variants and discount-scheme rows. Refused while stock is on hand, with a message that ends Adjust the stock to 0 first. | Requires products:product:delete |
| Customer or partner | Yes, with removeCustomer or removePartner. Refused while the partner has a draft, confirmed, partially invoiced or invoiced sales order (and, for removePartner, an open purchase order), with Cannot delete customer with active orders or Cannot delete partner with active orders. removeCustomer on a partner that is also a supplier removes only the customer role. | removeCustomer requires customers:customer:delete. removePartner accepts customers:customer:delete or suppliers:supplier:delete. |
| File upload | Use DELETE /api/upload/delete. See Files and exports. | See Files and exports |
A few rules follow from this:
- A deleted document keeps its document number. Expect test documents to use up numbers, so your numbering sequence can show gaps.
- Deletes are audited, so your administrators can see test activity.
- Stock movements, payments, invoices and credit or debit notes created by a test cannot simply be erased. Reverse them with the documented cancel, void or credit-note flows. See Returns and adjustments.
Removing a marked draft quote is one example. It requires sales:quote:delete.
mutation RemoveTestQuote($id: ID!) {
removeQuote(id: $id) {
id
}
}
7. Before you go live
- Store the host and the
organizationIdtogether for each workspace. - Always send
subdomain, as the bare label, when you log in from any host other than the workspace's own. - Never hard-code a custom domain as your API base.
- Run a read-only smoke test first: the health routes, then one read query. Do any write test only after that.
- Ask your administrator whether a non-production workspace exists before you run any write test.
For the full checklist, see the Production checklist.
What not to do
- Do not point your base URL at a shared app, sign-up or platform host and expect it to find your workspace. Those hosts carry no workspace, so login there needs
subdomain. - Do not call the API through a custom domain. A custom domain serves only the buyer portal.
- Do not send
subdomainas a full hostname, or with a-portalsuffix, and expect it to be kept. - Do not use the host as your tenant key, and do not cache it forever. Store
organizationId. - Do not rely on a login without
subdomain. The same email can exist in several workspaces. - Do not send a header such as
X-Organization-Idto choose a workspace. It is ignored by design. The workspace comes from the credential, or fromsubdomainat login. - Do not look for
gm_test_keys, a sandbox host or a test-mode flag. None exists, and agm_live_key writes to real data. - Do not write test data you cannot reverse. Invoices and payments are cancelled or voided, never deleted. Quotes and sales orders can be removed only while draft or cancelled, and the removes listed above are soft deletes.
- Do not use QA-only helpers that appear in the schema.
- Do not put test customers on real email addresses, and do not send documents or invites for test records.
Next: Production checklist.