---
title: "Package an existing service specification"
method: POST
path: "/service_specification/{id}/package"
tags: ["Service"]
---

# Package an existing service specification

`POST /service_specification/{id}/package`

## Request

- Base URL: `https://api.nullplatform.com`
- URL: `https://api.nullplatform.com/service_specification/{id}/package`
- Auth: HTTP bearer

Create a package from a service specification that doesn't belong to a package yet. The server assembles the bill of materials for you, in a single database transaction:

1. Pins the specification, each of its action specifications, each of its link specifications and each link's action specifications to a snapshot. An entity whose current content matches its latest snapshot reuses that snapshot; otherwise, including an entity with no snapshot yet, a new snapshot is minted.
2. Resolves `sources`. An inline source (`type` and `meta`) registers the artifact, or a new revision of it, under `nrn`. A referenced source (`artifact_id`) pins an existing artifact at `revision_id`, or at its newest revision when you omit it.
3. Creates the package and its first revision with these components:
   - `spec`: the service specification.
   - One component per source, named after the source (`source` when there's a single unnamed source).
   - One component per action specification, named after its slug.
   - One component per link specification, named after its slug.
   - One component per link action specification, named `<link slug>:<action slug>`.

The package's `name` and `slug` default to the specification's, and `visible_to` defaults to `[nrn]`. The response is the same package `POST /packages` returns.

> ℹ️ **Note:** `nrn` must be concrete (no wildcards). The request fails with `409` when the specification already belongs to a package; to publish a new revision of that package, use `PATCH /packages/{id}`.

## Path parameters

- `id` string, uuid, required

## Request body

- PackageFromSpec
  - `nrn` string, required: The owning NRN of the new package. Must be concrete (no wildcards).
  - `name` string: Package name. Defaults to the specification's name.
  - `slug` string: Package slug, unique per NRN. Defaults to the specification's slug.
  - `visible_to` string[]: NRNs allowed to consume the package. Defaults to [nrn].
  - `version` string: Semver of the first revision.
  - `sources` PackageFromSpecSource[], required: The artifacts to include in the bill of materials. With a single source, name is optional and defaults to "source"; with several, every source needs a unique name. "spec" is reserved.
    - union: Either an inline source that registers an artifact (type and meta) or a reference to an existing artifact (artifact_id). The two shapes are mutually exclusive.
      - object
        - `name` string: Component name in the bill of materials.
        - `type` 'oci_image' | 'oras_artifact' | 'git_repository' | 'blob', required: The artifact type.
        - `meta` object, required: The per-type artifact meta blob, validated like POST /artifacts. The artifact, or a new revision of it, is registered under the package's nrn.
        - `visible_to` string[]: Visibility of the registered artifact. A new artifact defaults to [nrn]; on an existing artifact, a non-empty value replaces its visibility.
      - object
        - `name` string: Component name in the bill of materials.
        - `artifact_id` string, uuid, required: An existing artifact you can read, possibly owned elsewhere or published globally.
        - `revision_id` string, uuid: The artifact revision to pin. Defaults to the artifact's newest revision.
        - `visible_to` string[]: Ignored for referenced artifacts: the referenced artifact isn't modified.

## Response `200`

The created package.

- Package
  - `id` string, uuid: The package id.
  - `nrn` string: The owning NRN.
  - `slug` string: URL-safe identifier, unique per NRN. Derived from name when omitted on create.
  - `name` string: Human-readable display name.
  - `visible_to` string[]: NRNs allowed to consume (read and link) this package. Supports trailing-wildcard scopes (for example, "organization=1:account=*") and the global wildcard "organization=*". Defaults to [nrn]. Modifications stay gated on the owning NRN.
  - `default_revision_id` string, uuid, nullable: Pinned revision UUID, or null to follow latest_revision_id.
  - `default_version` string, nullable: Server-derived semver of default_revision_id.
  - `latest_revision_id` string, uuid, nullable: Highest-semver revision UUID. Server-managed.
  - `latest_version` string, nullable: Server-derived semver of latest_revision_id.
  - `components` PackageComponent[]: The bill of materials of the resolved revision (default_revision_id, or latest_revision_id when no default is pinned).
    - `name` string, required: Name of the component within the revision, for example "spec" or "runtime:main". Unique within the revision; opaque to the package server.
    - `resource_type` string, required: Routing key that identifies which owning service resolves resource_id, for example "service_specification", "action_specification", "link_specification" or "artifact".
    - `resource_id` string, uuid, required: UUID of the underlying resource at its owning service.
    - `resource_revision_id` string, uuid, required: UUID of the revision this component pins: a specification snapshot for service, action and link specifications, or an artifact revision for artifacts.
    - `parent_id` string, uuid, nullable: The resource_id of the parent component within this revision. Required for action_specification components (the parent is a service or link specification component) and link_specification components (the parent is a service specification component); null for service_specification and artifact components.
  - `created_at` string, date-time: When the package was created.
  - `updated_at` string, date-time: When the package envelope last changed.

## Other responses

- `409`: The service specification already belongs to a package.
- `4XX`: Client error responses due to invalid input, missing parameters or insufficient permissions.
- `5XX`: Server error responses.

## Changes

- **2026-09-17** `43b3c1ecdfd8`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/nullplatform/apis/nullplatform-api/changes/service_specification/:id/package/post.md)

---

[API](https://skmtc.dev/nullplatform/apis/nullplatform-api.md) - [All operations](https://skmtc.dev/nullplatform/apis/nullplatform-api/llms.txt) - [OpenAPI document](https://skmtc.dev/nullplatform/apis/nullplatform-api/revisions/d3f66bc633d4?raw)
