Skip to Content

Services API

Services represent applications, microservices, or software components running on your infrastructure. This reference covers service CRUD, servers (service floors), connections, networks and their attachments, import/export, and auto-discovery.

Base URL

https://app.quazzar.space

All requests require an Authorization: Bearer <token> header. See the Authentication guide.

Every collection endpoint here returns a plain JSON array, not a paginated envelope, and every one is scoped by required project_id and environment_id query parameters.

List Services

GET /services

curl -H "Authorization: Bearer YOUR_TOKEN" \ "https://app.quazzar.space/services?project_id=PROJECT_UUID&environment_id=ENV_UUID"

Query Parameters:

ParameterTypeDescription
project_iduuidRequired
environment_iduuidRequired
namestringCase-insensitive substring match on the service name
tagsstring (repeatable)Match any of these tags
languagestringProgramming language
locationstringCase-insensitive contains across provider, region, and zone
limitintegerDefault 200, between 1 and 1000
offsetintegerDefault 0
updated_sinceISO 8601Only services modified since; includes soft-deleted ones

Paging is limit/offset, not page/per_page, and the parameter for a name search is name, not search. There is no group_id filter.

Response (200 OK):

[ { "id": "svc-uuid-1", "name": "auth-service", "owner_user_id": "user-uuid", "project_id": "project-uuid", "environment_id": "env-uuid", "service_floor_id": "floor-uuid", "group_id": "group-uuid", "subgroup_id": null, "group_name": "Backend", "subgroup_name": null, "type": "vm", "category": "api", "replicas": 3, "provider": "aws", "provider_region": "us-east-1", "provider_zone": "us-east-1a", "jira_project_key": "AUTH", "port": 8080, "ip_address": "10.0.1.20", "agent_id": null, "responsible_project_user_id": null, "responsible_user_id": null, "responsible_name": null, "programming_language": "python", "programming_language_version": "3.12", "repository_url": "https://github.com/org/auth-service", "version": "3.2.0", "links": [], "tags": ["critical", "auth"], "repository_deps": [], "updated_at": "2026-09-01T12:00:00Z", "deleted_at": null } ]

A few field names differ from what you might expect: the language is programming_language (not language), there is a single port rather than a ports array, and there is no description field. type is derived from the linked service floor and reads serverless when there is none. group_name and subgroup_name are resolved server-side so a client never has to join.

If environment_id does not belong to project_id, the response is 400 Environment not found or does not belong to project.

Create Service

POST /services

curl -X POST https://app.quazzar.space/services \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "auth-service", "project_id": "project-uuid", "environment_id": "env-uuid", "group_id": "group-uuid", "service_floor_id": "floor-uuid", "category": "api", "replicas": 3, "provider": "aws", "provider_region": "us-east-1", "programming_language": "python", "programming_language_version": "3.12", "repository_url": "https://github.com/org/auth-service", "version": "3.2.0", "jira_project_key": "AUTH", "port": 8080, "tags": ["critical", "auth"] }'

name, project_id, and environment_id are required; everything else is optional. Response 201 Created with the service object.

Validation worth knowing before you send a request:

  • repository_url must start with https:// or git://.
  • Every entry in links must start with http:// or https://.
  • category is a free-form label of at most 64 characters.
  • provider must be one of the supported providers.
  • The service owner is always the calling account; owner_user_id in the body is ignored.

Refusals:

StatusDetail
400Environment not found or does not belong to project
403Insufficient permissions to create service in this environment — requires project owner or an administrator/manager environment role
403Service limit reached for your '<plan>' plan (max N per project including add-ons)...
404Project not found
422Validation error

Service limits

The plan’s service allowance is enforced twice on the same number: once as a per-project cap and once as a per-environment cap. Quota add-ons on max_services raise both.

PlanMax services
Free5
Starter25
Team100
Business500
Enterprise2000
Enterprise+Unlimited

Get, Update, Delete a Service

EndpointNotes
GET /services/{service_id}Returns the full service object
PATCH /services/{service_id}Every field optional; project_id and environment_id cannot be changed
DELETE /services/{service_id}Removes the service and its connections and network attachments
GET /services/{service_id}/blast-radiusWhat else is affected if this service fails
GET /services/{service_id}/repository-depsDeclared repository dependencies

Service Connections

A connection joins two entities in the same environment. Either end may be a service or a service floor, which is why the ends are named a and b rather than source and target.

POST /service-connections

curl -X POST https://app.quazzar.space/service-connections \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "project_id": "project-uuid", "environment_id": "env-uuid", "a_type": "service", "a_id": "auth-service-uuid", "b_type": "service", "b_id": "user-database-uuid", "direction": "a_to_b", "name": "reads user data" }'
FieldValues
a_type, b_typeservice or floor
directionnone (default), a_to_b, b_to_a, both
chain_idOptional; groups connections that form one chain or hub
nameOptional label for the pair

There is no connection_type, port, or protocol on a connection — a port belongs to the service. Connecting an entity to itself is rejected at validation.

EndpointDescription
GET /service-connectionsList connections in a scope
POST /service-connections/batchCreate many at once; the batch shares one generated chain_id and a group_kind of chain or hub, while each item keeps its own direction
PATCH /service-connections/{connection_id}Change direction or name
DELETE /service-connections/{connection_id}Delete one connection
PATCH/DELETE /service-connections/chains/{chain_id}Operate on a whole chain or hub

/service-links is the simpler service-to-service edge list used by the dependency map.

curl -H "Authorization: Bearer YOUR_TOKEN" \ "https://app.quazzar.space/service-links?project_id=PROJ_UUID&environment_id=ENV_UUID"

POST /service-links takes {project_id, environment_id, from_service_id, to_service_id}. Both ends must be services, and they must differ.

Servers (Service Floors)

Servers represent compute — virtual machines, containers, or bare metal. The path is /service-floors with a hyphen.

POST /service-floors

curl -X POST https://app.quazzar.space/service-floors \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "project_id": "project-uuid", "environment_id": "env-uuid", "type": "vm", "hostname": "api-server-1", "provider": "aws", "provider_region": "us-east-1", "provider_zone": "us-east-1a", "provider_instance_id": "i-0123456789abcdef0", "ip_address": "10.0.1.10", "ip_addresses": ["10.0.1.10", "203.0.113.10"], "cpu": 4, "memory_mb": 16384, "storage_gb": 100, "instances": 1, "tags": ["api"] }'

project_id, environment_id, and type are required. A floor is identified by hostname, not by a name, and its specification is flat — cpu, memory_mb, storage_gb — not a nested specs object. Memory is in megabytes. There is no monthly_cost field; cost is estimated from the specification, see GET /projects/{project_id}/costs/estimate.

Server types:

TypeDescription
vmVirtual machine
containerContainer instance
baremetalPhysical dedicated server
otherAnything else

The bare-metal value is baremetal, one word.

Supported providers

aws, gcp, azure, hetzner, ovh, digitalocean, interserver, on-premise.

Any other value is rejected at validation on both services and floors.

GET /service-floors

curl -H "Authorization: Bearer YOUR_TOKEN" \ "https://app.quazzar.space/service-floors?project_id=PROJ_UUID&environment_id=ENV_UUID&type=vm"

project_id and environment_id are required. Filters: hostname (substring), tags, type, location (across provider/region/zone), zone, updated_since.

EndpointDescription
PATCH /service-floors/{floor_id}Update a floor
DELETE /service-floors/{floor_id}Delete a floor
GET /service-floors/{floor_id}/blast-radiusWhat depends on this server

Networks

POST /networks

curl -X POST https://app.quazzar.space/networks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "project_id": "project-uuid", "environment_id": "env-uuid", "name": "production-vpc", "cidr": "10.0.0.0/16", "gateway": "10.0.0.1", "vlan_id": 100, "is_private": true, "description": "Primary production network" }'

project_id, environment_id, name, and cidr are required. is_private defaults to true. There is no dns_servers field. Network names are unique within a (project, environment) pair.

GET /networks

project_id and environment_id are required. Filters: q (searches name, cidr, gateway, vlan, mask), vlan_id, mask (prefix bits, 0—32), range (CIDR range prefix, e.g. 10.0.0.0), updated_since.

PATCH /networks/{network_id} and DELETE /networks/{network_id} complete the set.

Network Attachments

Attaching networks to a service or a server is one endpoint family, /attachments. Both halves are GET to read and PATCH to replace the whole set.

EndpointDescription
GET /attachments/services/{service_id}/networksNetworks attached to a service
PATCH /attachments/services/{service_id}/networksReplace them
GET /attachments/service-floors/{floor_id}/networksNetworks attached to a server
PATCH /attachments/service-floors/{floor_id}/networksReplace them
curl -X PATCH https://app.quazzar.space/attachments/services/SVC_UUID/networks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"network_ids": ["net-uuid-1", "net-uuid-2"]}'

The body is the complete list — the previous attachments are replaced, so send everything you want to keep. At most 10 networks per service or per floor; a longer list is refused at 422 by request validation, before any lookup happens.

GET returns {service_id, networks: [...]} or {service_floor_id, networks: [...]} with full network objects.

A service or floor that does not exist, and one in a project the caller does not belong to, both answer the same 404 (Service not found / ServiceFloor not found) — see Errors & Permissions. Permission to change the attachments is checked after that and refuses at 403.

The web app used to have separate service-networks and floor-networks screens; those routes now redirect to the unified attachments screen. The API has always addressed both under /attachments.

Import / Export

Both endpoints are multipart/form-data, not JSON, both are asynchronous, and both answer 202 Accepted.

POST /impex/export

curl -X POST https://app.quazzar.space/impex/export \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "project_id=PROJECT_UUID" \ -F "environment_id=ENV_UUID"

Response (202 Accepted):

{ "status": "queued", "job_id": "job-uuid", "record_id": "record-uuid" }

The export runs in the background. Poll GET /impex/history?project_id={id} for its state; a finished record carries a signed download link, which GET /impex/download/{record_id} serves. That endpoint requires the sig and exp query parameters from the link and answers 410 Link expired past exp, so construct it from the record rather than by hand.

POST /impex/import

curl -X POST https://app.quazzar.space/impex/import \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "project_id=TARGET_PROJECT_UUID" \ -F "environment_id=TARGET_ENV_UUID" \ -F "[email protected]"

The configuration arrives as an uploaded file, not as a JSON body. The document contains the environment’s groups, subgroups, services, service floors, networks, service links, and network attachments; GET /impex/example returns a specimen of the expected shape.

Resources are matched by name — an existing resource with the same name is updated, a new one is created — and the import re-checks the plan’s per-environment service limit as it goes, so a document that would exceed it stops there.

Authorization for both: the caller must be a member of the project (a non-member gets 404 Project not found, the same answer as a project that does not exist), the environment must belong to that project (400 otherwise), and the caller must then be the project owner or hold an environment role (403 Not authorized for this environment).

EndpointDescription
GET /impex/history?project_id={id}Recent import/export records; optional environment_id, limit (1—200, default 50)
GET /impex/download/{record_id}?sig=...&exp=...Download a completed export via its signed link
GET /impex/exampleAn example document

Auto-Discovery

Discovery imports existing infrastructure from a connected cloud account. It is scoped to the cloud account, not to a provider name in a body.

POST /integrations/cloud-accounts/{account_id}/discover

curl -X POST https://app.quazzar.space/integrations/cloud-accounts/ACCOUNT_UUID/discover \ -H "Authorization: Bearer YOUR_TOKEN"

Response: {"status": "queued"}. There is no request body — the project and credentials come from the stored cloud account. Discovery runs as a background job and creates service floors for the resources it finds.

An account that does not exist and an account in someone else’s project both answer 404 CloudAccount not found — with the same detail deliberately, so the bodies cannot be compared to learn which uuids name real cloud accounts. Beyond that, the caller needs the connections.manage permission, because discovery decrypts and uses the credentials, and the account’s plan must include an integrations feature.

Connect an account first with POST /integrations/cloud-accounts, or through the per-provider endpoints (POST /integrations/cloud/{provider}/connect, GET /integrations/cloud/{provider}/status).

Next Steps