Skip to main content

Terraform model — Orbit resources as infrastructure

Terraform’s model is simple: declare the desired state of a resource, and the provider converges reality toward it. The Orbit Terraform provider applies that model to the CPaaS surface — webhook endpoints, messaging services, numbers, subaccounts, campaigns, and AI agents become HCL resources you version, review, and apply in CI. This page explains the model: what the provider is, which API surface it governs, and how plan/state semantics map onto the REST API. For copy-pasteable HCL and per-resource attributes, use the Terraform provider guide.

What “Orbit as IaC” means

The provider is a thin wrapper over the official Orbit Go SDK. Its entire job is lifecycle framing:
  1. One client. At provider-configure time it builds a single SDK client — one API key, one optional base-URL override — and hands that client to every resource.
  2. One schema per resource. Each resource declares a Terraform schema (required, optional, computed attributes) whose fields mirror the OpenAPI shape of the corresponding REST resource one-to-one.
  3. Verb mapping. Terraform’s four lifecycle hooks (Create, Read, Update, Delete) call the corresponding HTTP verb on the API path for that resource. No Terraform-specific magic exists anywhere — the provider sends exactly the requests you would send with the SDK by hand.
That thinness is the design. The authoritative contract stays the published REST API and its OpenAPI document; Terraform is one more client of that contract, so behaviour you verify with curl holds under terraform apply, and every new API field the REST surface ships is a candidate attribute rather than a new integration to build.

The resource inventory

Six resources cover the manage-as-code surface: What is deliberately excluded, and why:
  • Outbound voice and SMS termination. Carrier routing on Orbit is owned by the platform softswitch — no Terraform resource sets an outbound carrier, SIP trunk, or messaging route, and none ever will. orbit_number manages inbound routing only.
  • Runtime actions. Campaign sending, agent invocation, live messaging — anything imperative — stays out of the declarative lifecycle. Terraform manages durable configuration, not runtime triggers.
  • Nested agent internals. An agent’s tool registry and knowledge-base bindings have their own surfaces; orbit_agent manages the portable agent config (name, prompt, model, generation settings) and deliberately stops there.
  • Read-most surfaces. Dashboard-only conveniences with no stable idempotent mutation surface (onboarding wizards, playground state) are not Terraform material.
The exclusion list is the invariant that keeps the provider honest: if a resource cannot be expressed as create/read/update/delete against the REST contract, it does not get a Terraform resource.

The plan/state lifecycle

Every terraform plan is a diff between three things: your HCL, the last-known state, and a fresh read of the server. The mapping to the API is mechanical: Two mechanical rules fall out of this mapping:
  • Computed fields converge to the server. Fields marked computed (server-assigned ids, server-defaulted flags) are re-read on every refresh, so the state file always carries what the API returns — not what HCL last said.
  • Removed attributes are cleared explicitly. On update, attributes the server treats as nullable are sent as explicit null when HCL omits them. Without that rule, deleting an attribute from config would silently leave the old value server-side. With it, the next apply brings the server back to exactly what config declares.
terraform import <addr> <id> recovers drift at the whole-resource level: it fetches the resource’s current server state and captures it as the state baseline. Import does not write HCL — run terraform state show <addr>, copy the values into config, and the next plan goes empty.

API key resolution — argument vs environment

The provider accepts two configuration inputs, and resolution order is argument-first: api_key is a sensitive attribute — Terraform masks it from console output and state diagnostics. The CI-friendly pattern is to omit the argument entirely and set ORBIT_API_KEY in the pipeline’s secret store, so no key material sits in version control. base_url exists to point the provider at a staging Orbit deployment; production needs neither.

Drift-reconciliation semantics

Drift is any mutation made out-of-band — a dashboard edit, an SDK script, a support-console change. Reconciliation happens on refresh:
  1. Planned refresh. Every terraform plan re-reads each managed resource via GET, so a dashboard-side edit surfaces as a diff before you apply anything.
  2. Config wins. When you apply, the diff is resolved in the direction of HCL — the provider PATCHes the drifted attributes back to declared values.
  3. Deleted server-side → removed from state. If a refresh gets a 404, the resource is dropped from state and the next plan proposes recreating it — destroy-and-recreate is explicit in the plan output, never silent.
  4. Write-only fields cannot drift-check. Secrets the API never returns for reads (webhook signing secrets, subaccount API keys) are write-only by design: Terraform cannot detect out-of-band rotation of them. Rotate by updating the variable and re-applying.
  5. Force-replacement attributes. Where the API cannot mutate a field in place (a number’s phone_number, a subaccount’s slug), change means destroy + create. The plan names the replacement before approving.
Schedule terraform plan (or terraform plan -refresh-only) in CI to turn drift from a surprise at apply time into a report.

Before the registry release — build from source

The provider is early access. Until the registry listing ships, its source of truth is the provider source tree, and terraform init cannot pull a released binary. Locally:
Then install the binary into your Terraform plugin directory (or point dev_overrides in your Terraform CLI config at the built path) so terraform init resolves the local build. Once the registry.terraform.io/devotel/orbit listing publishes, the same config works with no source build.

Where to go next

The Terraform provider guide is the operational companion to this page: full HCL examples, the per-resource attribute tables, and the drift-and import patterns walked end-to-end. This page explained the model; the guide shows the config.

See also