> For the complete documentation index, see [llms.txt](https://experience-digital-transform.gitbook.io/experience-digital-transformation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://experience-digital-transform.gitbook.io/experience-digital-transformation/gif/module-4-architecture-and-technical-standards/4.5-generate-a-service-contract.md).

# 4.5 Generate a service contract

Turn a service brief into an OpenAPI contract, then an X-Road service description — the configuration that puts a service on the bus.

{% embed url="<https://youtu.be/sB3dhN7FdP8>" %}
From a service brief to an OpenAPI contract to a service on the bus — with AI (\~5 min)
{% endembed %}

**Watch first (\~5 min).** Then come back for the play.

> **Single message.** Turn a service brief into an OpenAPI contract, then an X-Road service description — the configuration that puts a service on the bus.

## The concept

To put a service on the bus, you need a contract — a precise description of what the service offers: the operations, the inputs, the outputs, the errors. That contract is an OpenAPI document, and it is the configuration that makes the service callable. As an architect, you generate it from a short service brief and the semantic map, and then turn it into the X-Road service description the bus actually uses to route a call.

An OpenAPI 3.x contract specifies, for one service, the operations it offers — say, 'get a learner's record' — the inputs each operation takes, the outputs it returns, the errors it can raise, and the security it requires. The outputs come straight from the semantic map you built: the contract is where the agreed meaning becomes a concrete, callable interface. A consumer reads the contract and knows exactly how to call the service — the field names, the types, the errors — without a single meeting.

The AI prompt for this video does the drafting. Give Claude the service brief — what the service does — and the semantic map for the data it returns, and it drafts the OpenAPI document: the paths, the request and response shapes, the error responses, the security scheme. As with the semantic map, every endpoint, field name and type is a \[confirm] until you check it against what the provider system actually exposes. An OpenAPI document that looks right but names a field the provider does not return is a contract that fails at the first real call — and looks fine until then.

The OpenAPI contract then becomes the X-Road service description — the form the bus uses to register the service and route a call to it. They are the same contract in two forms: OpenAPI for humans and tools to read, the X-Road service description for the bus to route. Generating the first and deriving the second is the core move of this whole knowledge product — using Claude to produce the configuration that puts a real service on the bus.

And this is why the contract is a build-pack artefact, not a diagram. It is executable configuration — it is what the consumer's code calls and what the bus routes. When you have done the semantic map and the service contract for one exchange, you hold, for that exchange, the agreed meaning and the callable interface: the technical-layer configuration the live demonstration runs on. That is real output, not a picture of output.

## Your turn — Generate an OpenAPI service contract

{% hint style="success" %}
**✍️ Drafting play · produces B24 OpenAPI service contract**

**Bring:** the service brief and the relevant semantic-map fields. From **B23, B22** (see [Your framework workbook](/experience-digital-transformation/gif/your-framework-workbook.md)). **Get:** an OpenAPI 3.x contract with \[confirm] placeholders, plus X-Road service-description notes. As text in the chat, not a file or a chart. **Time:** \~25 min · **Assistant:** any; the plays are tool-neutral. **With the kit:** no skill in this release, so the prompt runs bare in any assistant. **Watch for:** An OpenAPI contract that names fields the provider does not actually expose fails at the first real call — confirm every path and field against the provider system's real interface (not its documentation alone, which drifts) before publishing the contract or deriving the X-Road service description.
{% endhint %}

✍️ **Drafting play.** You give a few facts; the play produces a first draft of a document you will edit.

**What it does.** An architect needs an OpenAPI 3.x contract for a service, generated from the service brief and the semantic map, ready to derive an X-Road service description. This is the service-contract play.

{% tabs %}
{% tab title="Prompt" %}
Copy the prompt, replace the bracketed parts with your own context, and run it in the AI assistant of your choice. Never paste personal data or unpublished documents — see [How to use the plays](https://experience-digital-transform.gitbook.io/experience-digital-transformation/government-enterprise-architecture-gea/developing-a-gov-enterprise-architecture-gea/start-here/how-to-use-the-plays).

```
Generate an OpenAPI 3.x contract for a service on [country X]'s interoperability bus. Inputs: the service brief [paste: what the service does, e.g. 'return a learner's identity and enrolment given a national ID'] and the semantic map for the data it returns [paste the relevant fields, types and identifier from your semantic map]. Produce: (1) the path(s) and operation(s); (2) the request parameters, including the identifier; (3) the response schema, with field names and types taken from the semantic map; (4) the error responses; (5) the security scheme: on X-Road the security servers identify and authorise the calling member, so declare none unless the provider also needs an end-user token (then OAuth 2.x / OIDC). CRITICAL: mark every path, field name and type as [confirm: verify against what the provider system actually exposes] — do not invent fields the provider may not return. Then note which parts become the X-Road service description. Output: the OpenAPI document (YAML), a list of [confirm] items, and the X-Road service-description notes.
```

[Open in Claude](https://claude.ai/new?q=Generate%20an%20OpenAPI%203.x%20contract%20for%20a%20service%20on%20%5Bcountry%20X%5D%27s%20interoperability%20bus.%20Inputs%3A%20the%20service%20brief%20%5Bpaste%3A%20what%20the%20service%20does%2C%20e.g.%20%27return%20a%20learner%27s%20identity%20and%20enrolment%20given%20a%20national%20ID%27%5D%20and%20the%20semantic%20map%20for%20the%20data%20it%20returns%20%5Bpaste%20the%20relevant%20fields%2C%20types%20and%20identifier%20from%20your%20semantic%20map%5D.%20Produce%3A%20%281%29%20the%20path%28s%29%20and%20operation%28s%29%3B%20%282%29%20the%20request%20parameters%2C%20including%20the%20identifier%3B%20%283%29%20the%20response%20schema%2C%20with%20field%20names%20and%20types%20taken%20from%20the%20semantic%20map%3B%20%284%29%20the%20error%20responses%3B%20%285%29%20the%20security%20scheme%3A%20on%20X-Road%20the%20security%20servers%20identify%20and%20authorise%20the%20calling%20member%2C%20so%20declare%20none%20unless%20the%20provider%20also%20needs%20an%20end-user%20token%20%28then%20OAuth%202.x%20%2F%20OIDC%29.%20CRITICAL%3A%20mark%20every%20path%2C%20field%20name%20and%20type%20as%20%5Bconfirm%3A%20verify%20against%20what%20the%20provider%20system%20actually%20exposes%5D%20%E2%80%94%20do%20not%20invent%20fields%20the%20provider%20may%20not%20return.%20Then%20note%20which%20parts%20become%20the%20X-Road%20service%20description.%20Output%3A%20the%20OpenAPI%20document%20%28YAML%29%2C%20a%20list%20of%20%5Bconfirm%5D%20items%2C%20and%20the%20X-Road%20service-description%20notes.) *(experimental deep link: pre-fills the prompt in a new claude.ai chat; check the bracketed parts before sending)*

*Input, output and the kit skill are in the box above.*
{% endtab %}

{% tab title="Example input" %}
{% hint style="warning" %}
**Draft worked example, 2026-10-04.** A bare run on Progressa, annotated, but not yet checked against the kit's expected output. Read it with the annotations, not on its own.
{% endhint %}

The bracketed context in the prompt was replaced with the following:

**Service brief:** PNIA's identity service. Given a learner's National ID number (NIN), return only the identity fields PNEA needs to issue a senior-secondary certificate. Provider: PNIA, subsystem IDENTITY. Consumer: PNEA, subsystem EXAMS.

**From the semantic map (B23), entity `person`:** key `nin` (string, 11 digits); `given_name`, `family_name` (string); `date_of_birth` (date); `sex` (F or M); `region` (string). Not in the map, so not returned: mother's name, birth registration number, address.

**From the standards portfolio (B22):** OpenAPI 3.0.3 for contracts; callers authenticated by X-Road, OAuth not adopted inside the bus.
{% endtab %}

{% tab title="Example output" %}
{% hint style="warning" %}
**Draft: model output, unverified.** Read it with the annotations in the next tab, not on its own.
{% endhint %}

{% hint style="info" %}
**In the build pack.** The Progressa version of this artefact is a real file, not a pasted draft: `apps/specs/`, `prompts/` — see [What the build pack is](/experience-digital-transformation/gif/the-build-pack.md).
{% endhint %}

```yaml
openapi: 3.0.3
info: {title: PNIA identity API, version: "1.0"}
servers:
  - url: http://app-pnia:8000/v1          # [confirm] forwarding target
paths:
  /persons/{nin}:                          # [confirm]
    get:
      operationId: getPerson
      parameters:
        - {name: nin, in: path, required: true,
           schema: {type: string, pattern: '^[0-9]{11}$'}}
      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                required: [nin, given_name, family_name, date_of_birth, sex, region]
                properties:                # each field [confirm]
                  date_of_birth: {type: string, format: date}
                  sex: {type: string, enum: [F, M]}
                  # nin, given_name, family_name, region: string
        "400": {description: NIN not 11 digits}   # [confirm]
        "404": {description: No person with this NIN}
# security: none declared; X-Road identifies the caller (see B22)
```

**To confirm against PNIA's real interface:** the path; each field's name and type; whether the service returns 400 or rejects a bad NIN some other way; the forwarding URL.

**X-Road service-description notes:** register the document as type OPENAPI3 with service code `identity-api`; the security server reads `servers.url` as where to forward calls; each path and method becomes an endpoint the access-control list can grant.
{% endtab %}

{% tab title="Reading the output" %}
This is the teaching part of the play. Work through the output the way a chief architect would — what is earned, what is invented, what is missing.

<details>

<summary>1. Minimisation lives in the contract</summary>

The response lists six fields and nothing else, because the semantic map stopped at six. This is the same contract as the build pack's `apps/specs/pnia-identity.openapi.yaml`. There, PNIA's record holds three more fields that never reach the bus. Data protection by design is visible here, in a file, not in a policy.

</details>

<details>

<summary>2. The 400 response was invented</summary>

The run added a 400 for a malformed NIN. The pack's contract declares only 200 and 404, and the brief gave no basis for it. It is plausible, but a consumer that codes against a 400 PNIA never sends will mishandle the real error. This is the kind of field the \[confirm] marks exist for.

</details>

<details>

<summary>3. The forwarding URL is plain HTTP</summary>

`servers.url` is the hop from PNIA's security server to its own system, and it is `http`. The pack records this as an open production gap. The contract is where it becomes visible, so raise it here, with PNIA, before the description is registered.

</details>

{% hint style="danger" %}
**Safeguard for this play.** An OpenAPI contract that names fields the provider does not actually expose fails at the first real call — confirm every path and field against the provider system's real interface (not its documentation alone, which drifts) before publishing the contract or deriving the X-Road service description.
{% endhint %}
{% endtab %}

{% tab title="What next" %}
The contract goes to [4.7](/experience-digital-transformation/gif/module-4-architecture-and-technical-standards/4.7-wire-a-service-onto-the-bus.md), where it becomes the X-Road service description and the access-control entries. The build pack shows the finished pair; see [What the build pack is](/experience-digital-transformation/gif/the-build-pack.md). In your framework workbook this is artefact **B24**.

**This artefact feeds:** [4.7 Generate the X-Road service description and wiring checklist](/experience-digital-transformation/gif/module-4-architecture-and-technical-standards/4.7-wire-a-service-onto-the-bus.md)

See the full chain in [Your framework workbook](/experience-digital-transformation/gif/your-framework-workbook.md).
{% endtab %}
{% endtabs %}

## Sources

* OpenAPI 3.x specification — <https://spec.openapis.org/oas/latest.html>
* OAuth 2.x / OpenID Connect — <https://oauth.net/2/>
* NIIS X-Road service description (niis.org) — <https://docs.x-road.global>
* EU EIF — <https://interoperable-europe.ec.europa.eu/collection/nifo-national-interoperability-framework-observatory/european-interoperability-framework-detail>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://experience-digital-transform.gitbook.io/experience-digital-transformation/gif/module-4-architecture-and-technical-standards/4.5-generate-a-service-contract.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
