Skip to content
Docs BETA

Register a provisioner

Goal

For platform teams. In this task you tell the control plane about a provisioner, so that plan and apply can call it for the types that it handles. You also learn how to change a registration and how to remove it.

Before you start

Steps

Register in the dashboard

  1. Sign in to the Platform dashboard with the role operator.
  2. Open Provisioners, and choose Register a provisioner.
  3. Fill in the form:
Field What to write
Name Lower case letters and digits, with single hyphens between them, for example postgres-provisioner. Up to 255 characters.
URL The address of the provisioner, with no user name and no password.
Types One row for each pair. Choose the category (the form calls it Kind), and write the type. Use Add a type for more rows.
Async limit The longest wait, as an ISO 8601 duration such as PT10M. Leave it empty for a provisioner that answers at once.
  1. Choose Save. The dashboard shows the page of the new registration. If a field is wrong, an error summary lists each problem and moves the focus to it.

Register with the API

The registry is the resource /provisioners. The body is JSON. This example uses curl with bash. First read your token into a variable without showing it:

read -rs JOOMIZ_TOKEN
export JOOMIZ_TOKEN

Paste the token and press Enter. The curl option --variable '%JOOMIZ_TOKEN' reads the variable from the environment, and --expand-header puts it into the header. So the token is not on the command line. This needs curl 8.3 or later. Then send the registration. Write the address of your provisioner in url:

curl -s -w '\nHTTP %{http_code}\n' --variable '%JOOMIZ_TOKEN' --expand-header 'Authorization: Bearer {{JOOMIZ_TOKEN}}' -X POST "$JOOMIZ_SERVER_URL/provisioners" -H 'Content-Type: application/json' -d '{"name": "postgres-provisioner", "url": "https://postgres-provisioner.example.com", "types": [{"kind": "managed-resource", "type": "postgres"}]}'

The control plane answers 201 with the registration. The ID is made by the control plane:

{"id":"d7c1067f-0a27-4e0f-bab2-98e8eefa0d99","name":"postgres-provisioner","url":"https://postgres-provisioner.example.com","types":[{"kind":"managed-resource","type":"postgres"}],"createdAt":"2026-10-28T17:38:31.807Z","updatedAt":"2026-10-28T17:38:31.807Z","maxOperationDuration":null}
HTTP 201

The outputs on this page show example addresses in place of the addresses of the control plane and of the provisioner. The rules for the body are:

Field Rule
name Required. Lower case letters and digits, joined by single hyphens. At most 255 characters.
url Required. An http or https address with a host, at most 2048 characters, with no user name and no password.
types Required. From 1 to 50 pairs. Each pair has a kind and a type. A pair must not appear twice.
maxOperationDuration Optional. An ISO 8601 duration, more than zero and at most PT30M. Without it, the provisioner answers at once.

The category (kind) is one of target, tool, deployment, managed-resource and consumed-resource.

The control plane does not check that a type exists in the catalog when you register it. A type that is not in the catalog never receives a call.

Check

List the registrations. The answer holds every provisioner, with its types:

curl -s -w '\nHTTP %{http_code}\n' --variable '%JOOMIZ_TOKEN' --expand-header 'Authorization: Bearer {{JOOMIZ_TOKEN}}' "$JOOMIZ_SERVER_URL/provisioners"
{"items":[{"id":"d7c1067f-0a27-4e0f-bab2-98e8eefa0d99","name":"postgres-provisioner","url":"https://postgres-provisioner.example.com","types":[{"kind":"managed-resource","type":"postgres"}],"createdAt":"2026-10-28T17:38:31.807Z","updatedAt":"2026-10-28T17:38:31.807Z","maxOperationDuration":null}]}
HTTP 200

Read one registration with GET /provisioners/<id>. Replace <id> with the id of the registration. In the dashboard, the page Provisioners lists the same registrations, and you can filter them by name.

The audit trail records each change. An operator reads it with joomiz audit. The actions are provisioner.registered, provisioner.updated and provisioner.deregistered.

Change a registration

A change replaces the whole registration. Send PUT /provisioners/<id> with the complete body, or choose Edit in the dashboard. A field that you leave out is not kept. For example, a body without maxOperationDuration withdraws the support for slow work.

The answer to a change that sets a longest wait of 10 minutes shows the new value and the new time of the update:

{"id":"d7c1067f-0a27-4e0f-bab2-98e8eefa0d99","name":"postgres-provisioner","url":"https://postgres-provisioner.example.com","types":[{"kind":"managed-resource","type":"postgres"}],"createdAt":"2026-10-28T17:38:31.807Z","updatedAt":"2026-10-28T17:38:31.881Z","maxOperationDuration":"PT10M"}
HTTP 200

The pairs of the new body must be free. A pair that another provisioner handles is refused.

Remove a provisioner

You remove (deregister) a provisioner with DELETE /provisioners/<id>. The answer is 204 with no body. In the dashboard, choose Deregister on the page of the provisioner. The dialog lists the types, and it asks you to type the exact name of the provisioner.


HTTP 204

After the removal, every pair of the provisioner is free for a new registration. plan and apply for those types fail until another provisioner handles them. The control plane removes the registration even if applications use those types. Check your applications before you remove a provisioner.

A request for a registration that does not exist answers 404:

{"detail":"provisioner 'd7c1067f-0a27-4e0f-bab2-98e8eefa0d99' is not registered","status":404,"title":"Provisioner not found"}
HTTP 404

Troubleshooting

Answer Cause Fix
401 No token, or the token is not valid. Sign in again, and read a new token.
403 and Operator role required Your sign-in has no operator role. Ask for the role.
400 and provisioner name is invalid The name breaks the rule. Use lower case letters, digits and single hyphens.
400 and must be a syntactically valid absolute URL The address is not http or https, or it has no host. Write a complete https:// address.
400 and must not embed credentials The address has a user name or a password. Remove them from the address.
400 and points to an address that is not allowed The control plane may not call that address. Allow the network. Allow the address of your provisioner shows how.
400 and host could not be resolved The name does not resolve. Check the name and the DNS of the control plane.
400 and at least one {kind, type} pair is required The list types is empty. Name at least one pair.
400 and at most 50 {kind, type} pairs may be claimed The list types has more than 50 pairs. Register the other pairs with a second provisioner.
400 and kind ... is not supported The kind is not one of the five. Use a kind from the list above.
400 and listed more than once A pair appears twice in types. Write each pair once.
400 and maxOperationDuration The duration is not valid, is zero, or is over PT30M. Write a duration such as PT10M, or leave it out.
409 and already claimed by another provisioner Another provisioner handles the pair. Remove that registration first, or choose another type.
404 No registration has this ID. Check the ID in the list.

This is the answer for a pair that another provisioner handles:

{"detail":"already claimed by another provisioner: managed-resource/postgres","status":409,"title":"Provisioned type already claimed"}
HTTP 409

The dashboard does not show the text of an answer. It shows a short text of its own. For a refused value, it says that the control plane did not accept the values. It asks you to check the name, the URL, the types and the async limit. For a pair that another provisioner handles, it says that a type is already claimed. To read the exact reason, send the same registration with the API.

Next

Write a provisioner of your own in Build a provisioner with the starter.