---
sidebar_label: Service specifications
toc_max_heading_level: 3
doc_id: 06b238b9-ef42-4e29-8a5c-4633881017d7
description: >-
  Guide for designing and creating service specifications with schemas and API
  references
keywords:
  - service specification
  - JSON schema
  - UI schema
  - API reference
  - nullplatform
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Service specification

:::note Before you start

If you haven't done it yet, read about [services' main concepts](../getting-started.md#specifications).

Also keep these handy references:

* **Service Specification API**
  * [Our API reference](/docs/service-specification-api-index)
* **Schemas**
    * [JSON Schema (external link)](https://json-schema.org/learn/getting-started-step-by-step) | [Additional keywords supported by nullplatform](/docs/json-ui-schema/json-schema)
    * [UI Schema (external link)](https://jsonforms.io/docs/uischema/) | [How UI schema is integrated in nullplatform](/docs/json-ui-schema/overview)
      :::

## Design your service specification

Before you create the service, ask yourself a few questions. We’ll present them in a table so you
can
see how your answers impact your service specification:

| Question                                                        | Guidance                                                                                                                                                                                                                       |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| What's the name for the service?                                | Choose a natural name (e.g., MySQL) and set it into the `name` field.                                                                                                                                                          |
| Which properties do I need to hold for the service?             | These are the properties that define the service. They are usually a combination of input parameters plus values obtained from the cloud provider upon service creation. Set these as a JSON schema in the `attributes` field. |
| Where in the organization is the service going to be available? | A good default is to start by making the service visible everywhere with the value `organization=your-org-id:account=*`. Set your choice in the `visible_to` array.                                                            |
| What can be done with the service?                              | Define standard actions in the service specification to control what users can do with the service.                                                                                                                            |

## Defining standard actions in the service spec

New service specifications automatically generate their standard actions: **create**, **update**, and **delete** (`use_default_actions` defaults to `true`). The three actions are built from the spec's attributes schema and stay in sync with it as it evolves, with no manual upkeep.

To shape how the generated actions behave:

- Use the `visibleOn` and `editableOn` keywords in your schema to control when each property should be shown or editable during the instance lifecycle.

> For example:
>
> - `visibleOn: ["create", "update"]` makes a property visible when creating or updating an instance.
> - `editableOn: ["create"]` makes a property editable only at creation time.

You can check our [Special schema keys](/docs/json-ui-schema/json-schema) page for more information on these keywords. 

By using this approach, you manage everything from a single source of truth, the service specification schema, without the need to define or maintain separate action specifications.

:::info Autogenerated actions vs. Action specifications
We recommend keeping the autogenerated standard actions unless:

- You need to update your standard actions. **Autogenerated actions can't be edited directly**, so changes and updates must be made using action specifications.
- You prefer to manage the default **create**, **update**, and **delete** actions manually using **action specifications**.

In both cases, set `"use_default_actions": false` explicitly when creating the specification. On specs with autogenerated actions you can still add **custom** actions, but you can't override the generated `create`, `update`, or `delete`. Specifications created before this default changed keep the value they were saved with.

See [Action specifications](/docs/services/craft-a-service/service-actions) for more information on how to design actions for your services.
:::


## Automatic instance naming

New service specifications also default to `"use_default_naming": true`, which makes the `name` field optional when someone creates a service from your spec:

- If the caller provides a name, it's used as-is.
- If not, nullplatform generates one from the specification name plus a short suffix (like `redis a4f3`).
- If your attributes schema defines its own `name` property, its value becomes the instance name, and the two are kept in sync.

Set `"use_default_naming": false` if you want callers to always provide a name explicitly. Specifications created before this flag existed keep requiring a name.

## Create a service specification

You can create a service specification using our [CLI](/docs/cli/) or [API](/docs/api/service-specification-create).

:::tip Craft your service specifications from the UI
You can also review and edit service and link specifications directly from the UI, making it easier than ever to work with your services.

Go to **Platform settings > Services** and click **New service specification**.
:::

Here’s an example of a service specification that includes standard actions:

<Tabs
defaultValue="create-service-spec-cli"
values={[
{ label: 'CLI', value: 'create-service-spec-cli' },
{ label: 'cURL', value: 'create-service-spec-curl' },
]}>
<TabItem value="create-service-spec-cli">

    ```bash
    np service specification create \
     --body '{
        "name": "my-service-specification",
        "type": "dependency",
        "visible_to": [
          "organization=1:account=1",
          "organization=1:account=2"
        ],
        "use_default_actions": true,    // Default for new specs. Set to 'false' to define create/update/delete yourself
        "attributes": {         // Defines the structure and layout of the form
          "schema": {
            "type": "object",
            "properties": {
              "my_string_property": {
                "type": "string",
                "visibleOn": ["read", "create", "update"],
                "editableOn": ["create"]
              },
              "my_number_property": {
                "type": "number",
                "default": 0,
                "visibleOn": ["read", "create"],
                "editableOn": ["create"]
              }
            },
            "required": [
              "my_string_property"
            ]
          },
          "values": {}
        }
      }'
    ```

  </TabItem>
  <TabItem value="create-service-spec-curl">

  ```bash
  curl -L 'https://api.nullplatform.com/service_specification' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer the-token' \
  -d '{
    "name": "my-service-specification",
    "type": "dependency",
    "visible_to": [
      "organization=1:account=2:namespace=3",
      "organization=1:account=2:namespace=4"
    ],
    "use_default_actions": true,    // Default for new specs. Set to 'false' to define create/update/delete yourself
    "attributes": {     // Defines the structure and layout of the form
      "schema": {
        "type": "object",
        "properties": {
          "my_string_property": {
            "type": "string",
            "visibleOn": ["read", "create", "update"],
              "editableOn": ["create"]
          },
          "my_number_property": {
            "type": "number",
            "default": 0,
            "visibleOn": ["create", "update"],
              "editableOn": ["create"]
          }
        },
        "required": [
          "my_string_property"
        ]
      },
      "values": {}
    }
  }'
  ```

  </TabItem>
</Tabs>

:::info Service details
The JSON schema under `attributes` determines the service's attributes, fields, and layout in the UI. These properties appear in the **See details**, **Create**, or **Edit** views in the nullplatform interface.
You can further enhance the rendering of that section using UI Schema.
:::

#### Some attributes to consider

Let’s take a closer look at a few of the key fields in the JSON:

- **`visible_to`**: Defines visibility for the service using [NRN](../../NRN.md). Each entry in this array follows the
  format `organization=<org_id>:account=<account_id>:namespace=<namespace_id>`. For example:
    - `"organization=1:account=2:namespace=3"` makes the service visible to namespace `3` under account `2` of
      organization `1`.
    - `"organization=1:account=2:namespace=*"` makes the service visible to all namespaces under account `2`.
- **`use_default_actions`**: Autogenerates the standard `create`, `update`, and `delete` actions from the attributes schema and keeps them in sync with it. Defaults to `true` on new specifications; set it to `false` to define those actions yourself.
- **`use_default_naming`**: Makes the instance `name` optional: when no name is sent, nullplatform generates one or takes it from the schema's `name` property. Defaults to `true` on new specifications.
- **`attributes`**: Describes the configuration and layout of the service using JSON Schema.

You can always refer to our [API reference](/docs/api/service-specification-create) for more details on service specification requests.


## Restrict services to specific scopes

Not every service makes sense in every scope. For example, a *Helm Release Deployer* should only be available in
Kubernetes-based scopes, while an *Airflow operator* should only work in Airflow scopes.

To prevent mismatches, you can **restrict services and links to specific scope types or scope specifications**.
This ensures they are only visible and usable in the right environments.

**How it works**

You can restrict scope compatibility by defining the `scopes` attribute in your
**service specification or link specification**.

The `scopes` field supports scope types (like `AWS:SERVERLESS:LAMBDA` or `AZURE:WEB_POOL:AKS`), and scope spec ID.

```json
{
  "scopes": {
    "values": [
      "AWS:SERVERLESS:LAMBDA",
      "AWS:WEB_POOL:EC2INSTANCES",
      "uuid-of-a-specific-scope-specification"
    ]
  }
}
```

**Examples:**

- An Airflow operator service restricted to your `AIRFLOW:JOB` spec (UUID).
- A Helm Release Deployer restricted to `K8S:DEPLOYMENT` (UUID).
- An SQS Publisher restricted to `AWS:SERVERLESS:LAMBDA` to ensure it only appears where event-driven compute
  is supported.

> 📖 See our [API reference](/docs/api/service-specification-create) for more info on supported scope types and usage. 
