SDKs
Two typed clients, generated from the same API contract. Download them at zero.nnumbers.com.br/downloads.
The platform namespace does not resolve through npm install or go get — that is an architectural decision. What is distributed is the real consumption model: the packed npm package and the Go package to vendor by copy, exactly how the CLI and the MCP server consume the Go SDK.
TypeScript
npm install ./zero-sdk-typescript.tgz
import { ZeroClient } from '@nnumbers/zero';
const zero = new ZeroClient({
baseUrl: 'https://api.zero.nnumbers.com.br',
token: process.env.ZERO_TOKEN,
});
// deploy, with an idempotency key — repeating never duplicates
const deployment = await zero.createDeployment(service, {},
{ idempotencyKey: crypto.randomUUID() });
Go
tar -xzf zero-sdk-go.tar.gz -C internal/
import "yourcompany.com/app/internal/zero"
client, err := zero.New(zero.Config{
BaseURL: "https://api.zero.nnumbers.com.br",
Token: zero.StaticToken(os.Getenv("ZERO_TOKEN")),
})
What both guarantee
- Idempotency — every mutation accepts a key; repeating the call never creates two resources.
- Typed errors — the platform's error catalog becomes a type, with the code and the suggested action.
- Live following — an operation's stream arrives as an iterable (TypeScript) or a channel (Go), without you assembling SSE by hand.
Fields that may be null
A field the API may return as null is typed as nullable: T | null in TypeScript, a pointer in Go. The compiler now requires you to handle the null — in Go, check for nil before dereferencing; in TypeScript, narrow the type before using the value.
Networking Between Projects
Both SDKs have the operations of networking between projects: networks, the environment in the network, permissions, project exposure, internal name, and Gateway internal entries.
const network = await zero.createNetwork(organization, { name: 'payments' },
{ idempotencyKey: crypto.randomUUID() });
await zero.attachEnvironmentNetwork(environment, { network_id: network.id });
await zero.setServiceInternalName(service, { name: 'api' }); // api.zero.internal
Two lists mix shapes, and the kind field says which each item is: a network's permissions (service_grant or gateway_entry_grant) and who comes into a project (service_grant or private_route). In TypeScript, the union narrows by kind, with no cast:
for (const item of (await zero.listNetworkAccessGrants(network.id)).items) {
if (item.kind === 'service_grant') console.log(item.target_service_name, item.port);
else console.log(item.entry_address);
}
In Go, the items arrive as json.RawMessage, and zero.DecodificarPermissaoDaRede and zero.DecodificarChegadaAoProjeto split them by kind. A kind your SDK version does not know comes back with no shape and no error, with the JSON in Bruto — the listing does not break because of a new type.
The network of an environment outside any network comes back null: network is null in TypeScript and nil in Go.
Changing the address
setCanonicalDomain (TypeScript) and SetCanonicalDomain (Go) change the project's public address. Besides the new address, the response carries:
| Field | What it is |
|---|---|
operation_id | The deployment that puts the new address live — follow it to the end. Null when the project has never deployed: the previous address is released immediately and there is nothing to follow |
previous | The previous address. May be null |
While the deployment runs, the previous address appears among the project's domains with the state releasing: it answers until the deployment with the new address becomes ready, and is then released.
Was this page helpful?
Report a problem on this pageDo not send passwords, keys, tokens, or customer data.