openapi: 3.1.0
info:
  title: Interactie API
  description: |
    De **Interactie API** standaardiseert hoe portalen klantgerelateerde gegevens tonen en acties uitvoeren namens hun gebruikers. Het doel: een eindgebruiker kan informatie en taken raadplegen op elk aangesloten portaal, ongeacht welke organisatie de gegevens beheert — vergelijkbaar met hoe je met je telefoon in het buitenland belt via een ander netwerk, zonder dat je iets hoeft in te stellen.

    De API is opgezet als een **kern met uitbreidingssets**: het `/context/zoek`-endpoint retourneert een samengesteld object waarvan de inhoud via de `include`-parameter wordt gestuurd. Initieel worden taken ondersteund; andere resource-typen (bijv. contactmomenten, zaakstatus) kunnen worden toegevoegd zonder bestaande implementaties te breken.

    Identificatie en authenticatie van eindgebruikers vallen buiten de scope van dit contract; die worden afgehandeld door een externe identity provider (zoals DigiD of eHerkenning).

    ## Principes

    ### 1. Uniform contract

    Alle providers implementeren dezelfde interface: dezelfde endpoints, dezelfde responsestructuur, hetzelfde gedrag. Het maakt voor een portaal niet uit hoe de provider intern is georganiseerd of waar de data vandaan komt.

    ### 2. Backward en forward compatibility

    Portalen en providers moeten onafhankelijk van elkaar kunnen evolueren. Het contract is daarom uitbreidbaar zonder bestaande implementaties te breken:

    - **Backward compatible** — een ouder portaal werkt correct met een nieuwere provider (de provider breekt geen bestaande portalen bij het toevoegen van velden). De provider mag geen velden verwijderen, hernoemen of verplichte request-parameters toevoegen.
    - **Forward compatible** — een huidig portaal werkt correct met een toekomstige provider die nieuwe velden toevoegt (het portaal negeert wat het niet herkent). Het portaal móet onbekende velden in de response negeren.

    Dit volgt het [Robustness principle](https://en.wikipedia.org/wiki/Robustness_principle): wees conservatief in wat je stuurt, liberaal in wat je accepteert.

    ### 3. Uitvoering bij de bron

    Een taak wordt altijd uitgevoerd bij de bron. Portalen tonen de taak, maar slaan geen uitvoeringsdata op. Dit borgt dataminimalisatie: uitvoeringsdata verlaat de bron niet structureel.
  version: 0.1.0
  contact:
    name: VNG Realisatie
    url: https://github.com/vng-realisatie/mijn-taken-api
    email: standaarden.ondersteuning@vng.nl
  license:
    name: EUPL 1.2
    url: https://opensource.org/licenses/EUPL-1.2
  x-logo:
    url: https://vng-realisatie.github.io/mijn-taken-api/img/mijn-taken.svg
    altText: MijnTaken API

servers:
  - url: https://api.example.nl/v1
    description: Productie

tags:
  - name: Operaties
    description: Operaties van de kernmodule.
  - name: Schema's
    description: Gedeelde schema's van de kernmodule.
  - name: Taken
    description: Operaties en schema's van de uitbreidingsmodule Taken.

x-tagGroups:
  - name: Kernmodule
    tags:
      - Operaties
      - Schema's
  - name: Uitbreidingsmodules
    tags:
      - Taken

paths:
  /context/zoek:
    post:
      operationId: zoekContext
      summary: Zoek context
      description: |
        Retourneert een samengesteld object met klantgerelateerde gegevens voor
        de opgegeven klant en optionele context. Via de `include`-parameter
        bepaalt de aanroeper welke resource-typen worden meegeleverd.

        Dit endpoint is bedoeld om verschillende overzichts- en contextschermen
        te bedienen vanuit één call. Voorbeelden:

        | Scherm | `contextId` | `include` |
        |---|---|---|
        | MijnTaken | — | `["taken"]` |
        | Overzicht per domein | — | `["taken", "contactmomenten"]` |
        | Zaak-contextscherm | `urn:nl:gemeenten:zaak:2026-00042` | `["taken", "contactmomenten"]` |

        Initieel wordt alleen `taken` ondersteund. Nieuwe resource-typen
        kunnen worden toegevoegd zonder bestaande implementaties te breken:
        clients negeren typen die ze niet kennen.

        **Ontwerpkeuze — POST als query:**
        Deze operatie gebruikt `POST` omdat de filtercriteria in de request body
        worden meegegeven. Qua semantiek gedraagt de operatie zich als de
        [HTTP QUERY method (IETF-draft)](https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-05.txt):
        zij is **safe** — de aanroep heeft geen neveneffecten en wijzigt geen
        toestand op de server. Portalen mogen de aanroep dan ook als zodanig
        behandelen — herhalen bij netwerkstoringen, optimistisch prefetchen, etc.
        Zodra HTTP QUERY breed ondersteund wordt door clients en gateways, kan
        dit endpoint zonder gedragswijziging worden gemigreerd.
      tags:
        - Operaties
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContextQuery"
      responses:
        "200":
          description: Succesvol
          headers:
            API-Version:
              $ref: "#/components/headers/API-Version"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContextResultaat"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /taken/{uuid}:
    parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
          format: uuid
        example: 550e8400-e29b-41d4-a716-446655440000

    get:
      operationId: retrieveTaak
      summary: Haal één taak op
      description: |
        Geeft de volledige representatie van één taak terug, inclusief
        type-specifieke uitvoeringsvelden (bijv. uploadconfiguratie of
        formulierdefinitie). Gebruik dit endpoint vlak vóór de uitvoering.
      tags:
        - Taken
      responses:
        "200":
          description: Succesvol
          headers:
            API-Version:
              $ref: "#/components/headers/API-Version"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Taak"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  schemas:
    ContextQuery:
      x-tags:
        - Schema's
      type: object
      description: Zoekcriteria voor het opvragen van een samengestelde contextrespons.
      required:
        - klantId
      properties:
        klantId:
          type: string
          description: Identifier van de klant (bijv. sub-claim uit het ID-token).
          example: "a8f3c1d2-7e44-4b1a-9c0f-123456789abc"
        contextId:
          type: string
          format: uri
          pattern: "^urn:"
          description: |
            URN-identifier van de context waarbinnen gezocht wordt
            (bijv. een zaak, product of dossier). Het URN-formaat maakt
            de identifier namespace-safe en onafhankelijk van het type context.
          example: "urn:nl:gemeenten:zaak:2026-00042"
        include:
          type: array
          description: |
            Resource-typen waarvan een **samenvatting** wordt meegeleverd in de respons.
            Als afwezig of leeg, worden alle door de provider ondersteunde typen
            teruggegeven. Clients negeren typen die ze niet kennen.

            Let op: de respons bevat altijd samenvattingen (info-objecten). Voor resource-typen
            waarvoor een detail-endpoint bestaat (bijv. `GET /taken/{uuid}` voor
            afhandeling) kan aanvullende informatie worden opgevraagd.

            Open lijst — nieuwe waarden kunnen worden toegevoegd zonder
            bestaande implementaties te breken.
          items:
            type: string
            description: "[Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112)."
            examples:
              - taken
            x-enumDescriptions:
              taken: Taken die de klant kan uitvoeren (bijv. uploaden, betalen, formulier invullen).
          example: ["taken"]

    ContextResultaat:
      x-tags:
        - Schema's
      type: object
      description: |
        Samengesteld object met klantgerelateerde gegevens.
        Bevat per resource-type een lijst van **samenvattingen** (info-objecten). Voor resource-typen
        waarvoor een detail-endpoint bestaat (bijv. `GET /taken/{uuid}` voor afhandeling)
        kan aanvullende informatie worden opgevraagd.
        Bevat alleen de resource-typen die via `include` zijn opgevraagd
        én door de provider worden ondersteund. Afwezige sleutels betekenen
        dat het type niet is opgevraagd of niet ondersteund wordt.
      properties:
        taken:
          type: array
          description: |
            Taken die voldoen aan de opgegeven filtercriteria. Aanwezig als
            `taken` in `include` staat (of `include` afwezig is).

            Bevat `TaakInfo`-objecten: alle velden die nodig zijn voor
            een takenlijst of taakkaart. Type-specifieke uitvoeringsvelden
            worden niet meegeleverd; die zijn beschikbaar via `GET /taken/{uuid}`.
          items:
            $ref: "#/components/schemas/TaakInfo"

    ContextLink:
      x-tags:
        - Schema's
      type: object
      description: |
        De context waaraan de taak is gekoppeld (bijv. een zaak of product),
        aangeduid met een **URN** en optioneel een **canonicalUrl**.

        `canonicalUrl` is afwezig als de dienstverlener geen eigen portaal heeft
        (bijv. bedrijfsonderdelen die geen eigen portaal hebben).

        Portalen die de URN kunnen omzetten naar een interne URL doen dat;
        `canonicalUrl` is dan irrelevant. Anders geldt: gebruik `canonicalUrl`
        als die aanwezig is. Het type context is af te leiden uit de URN-structuur.

        ```mermaid
        flowchart TD
            A{URN → Interne URL?} -->|Ja| B[Gebruik interne URL]
            A -->|Nee| C{Heeft canonicalUrl?}
            C -->|Ja| D[Gebruik canonicalUrl]
            C -->|Nee| E[Nader te bepalen]
        ```
      required:
        - urn
      properties:
        urn:
          type: string
          pattern: "^urn:"
          description: Portaal-agnostische identifier van de context, in URN-formaat.
          example: "urn:nl:gemeenten:zaak:2026-00042"
        canonicalUrl:
          type: string
          format: uri
          description: |
            Optionele URL naar de context in de omgeving van de dienstverlener.
            Afwezig als de dienstverlener geen eigen portaal heeft. Als aanwezig,
            bruikbaar als directe link voor portalen die de URN niet zelf omzetten.
            Portalen met eigen contextondersteuning kunnen deze URL negeren en een
            interne URL opbouwen via de URN.
          example: "https://portaal.gemeente.nl/zaken/2026-00042"

    UitvoeringInfo:
      x-tags:
        - Taken
      type: object
      description: |
        Uitvoeringsinformatie voor een taak: hoe de actieknop gelabeld wordt
        en waar de gebruiker naartoe gaat.

        `fallbackUrl` is altijd aanwezig als universele fallback.

        ```mermaid
        flowchart TD
            A{Type ondersteund?} -->|Nee| B[Label: \'Uitvoeren\']
            A -->|Ja| C[Type-specifiek label]
            B --> D[Redirect via fallbackUrl + returnUrl]
            C --> E{Type-specifieke velden aanwezig?}
            E -->|Nee| D
            E -->|Ja| F[Lokale uitvoering via type-specifieke velden]
        ```
      required:
        - fallbackUrl
      properties:
        type:
          type: string
        fallbackUrl:
          type: string
          format: uri
          description: |
            Fallback-URL voor de uitvoering van de taak. Altijd aanwezig.
            Gebruikt als het portaal `type` niet ondersteunt of de type-specifieke
            velden niet lokaal kan verwerken.

            In redirect-scenario\'s voegt het portaal een `returnUrl` toe
            vóór de redirect, zodat de externe partij de gebruiker na afloop
            terug kan sturen naar `SCR-TAKEN-IN-CONTEXT`. De precieze
            parameternaam is afhankelijk van de ontvangende partij.
          examples:
            - "https://portaal.gemeente.nl/taken/550e8400/uitvoeren?returnUrl=https%3A%2F%2Fmijnoverheid.nl%2Ftaken%2Fcontext%2Fzaak-2026-00042"
            - "https://betalen.gemeente.nl/pay?kenmerk=REF-2026-00042&bedrag=6550&returnUrl=https%3A%2F%2Fmijnoverheid.nl%2Ftaken%2Fcontext%2Fzaak-2026-00042"
    Uitvoering:
      x-tags:
        - Taken
      description: |
        Uitvoering van een taak. Bekende typen hebben een variant met een `const`-waarde op
        `type` en type-specifieke velden. `OnbekendeUitvoering` dient als catch-all voor
        toekomstige of onbekende typen en zorgt ervoor dat `oneOf` altijd precies één match
        oplevert.

        Discriminator: veld `type`.
      oneOf:
        - $ref: "#/components/schemas/UploadUitvoering"
        - $ref: "#/components/schemas/FormulierUitvoering"
        - $ref: "#/components/schemas/BetaalUitvoering"
        - $ref: "#/components/schemas/OnbekendeUitvoering"

    UploadUitvoering:
      x-tags:
        - Taken
      allOf:
        - $ref: "#/components/schemas/UitvoeringInfo"
        - type: object
          description: "Uitvoering voor `type: upload`."
          required:
            - submitUrl
          properties:
            type:
              const: upload
            submitUrl:
              type: string
              format: uri
              description: API-endpoint voor het uploaden van bestanden (niet zichtbaar voor de gebruiker).
              example: "https://api.gemeente.nl/documenten/upload"
            maxBestanden:
              type: integer
              description: Maximum aantal bestanden dat geüpload mag worden.
              example: 5
            toegestaneFormaten:
              type: array
              items:
                type: string
              description: Toegestane bestandsextensies.
              example: [pdf, jpg, png]
            maxBestandsgrootte:
              type: integer
              description: Maximale bestandsgrootte in bytes.
              example: 10485760

    FormulierUitvoering:
      x-tags:
        - Taken
      allOf:
        - $ref: "#/components/schemas/UitvoeringInfo"
        - type: object
          description: "Uitvoering voor `type: formulier`."
          required:
            - submitUrl
          properties:
            type:
              const: formulier
            submitUrl:
              type: string
              format: uri
              description: API-endpoint voor het indienen van het formulier (niet zichtbaar voor de gebruiker).
              example: "https://api.gemeente.nl/formulieren/indienen"
            schema:
              type: object
              description: Formulierconfiguratie (JSON Schema of vergelijkbaar). Structuur nog nader te bepalen.

    BetaalUitvoering:
      x-tags:
        - Taken
      allOf:
        - $ref: "#/components/schemas/UitvoeringInfo"
        - type: object
          description: |
            Uitvoering voor `type: betaling`.
            De redirect-URL voor de betaalprovider is opgenomen in `fallbackUrl`.
          properties:
            type:
              const: betaling

    OnbekendeUitvoering:
      x-tags:
        - Taken
      allOf:
        - $ref: "#/components/schemas/UitvoeringInfo"
        - type: object
          description: |
            Catch-all voor uitvoeringstypen die niet als bekende variant zijn gedefinieerd.
            Matcht wanneer `type` geen van de bekende waarden (`upload`, `formulier`, `betaling`) heeft.
          properties:
            type:
              not:
                enum:
                  - upload
                  - formulier
                  - betaling
              example: "nieuw-type"
              description: |
                **Geen vaste waarde** — dit veld is hier niet `upload`, `formulier` of `betaling`.

                Type van de uitvoering, afkomstig uit de taakdefinitie.
                Het portaal leidt hier het label voor de actieknop uit af. Onbekend of afwezig
                type → generiek label „Uitvoeren“ + `fallbackUrl` als fallback.

    TaakInfo:
      x-tags:
        - Taken
      type: object
      description: |
        Taakrepresentatie zoals teruggegeven door `POST /context/zoek`.
        Bevat alle velden die nodig zijn voor een takenlijst of taakkaart,
        inclusief context en uitvoeringstype. Type-specifieke uitvoeringsvelden
        worden niet meegeleverd.
      required:
        - uuid
        - titel
        - status
      properties:
        uuid:
          type: string
          format: uuid
          readOnly: true
          example: 550e8400-e29b-41d4-a716-446655440000
        titel:
          type: object
          description: |
            Taaknaam in één of meer talen (BCP 47 taalcodes als sleutels).
            Nederlands (`nl`) is verplicht; andere talen zijn optioneel.
            Portalen tonen de taal die overeenkomt met de voorkeur van de
            gebruiker, of vallen terug op `nl`.
          required:
            - nl
          properties:
            nl:
              type: string
              description: Nederlandse taaknaam (verplicht).
          additionalProperties:
            type: string
          example:
            nl: Kentekenbewijs uploaden
            en: Upload vehicle registration document
        toelichting:
          type: object
          description: |
            Optionele korte toelichting op de taak, in één of meer talen
            (BCP 47 taalcodes als sleutels). Alleen aanwezig als de titel
            niet voldoende zelfbeschrijvend is. Als `toelichting` aanwezig is,
            moet ten minste de Nederlandse variant (`nl`) gevuld zijn.
          required:
            - nl
          properties:
            nl:
              type: string
              description: Nederlandse toelichting (verplicht als `toelichting` aanwezig is).
          additionalProperties:
            type: string
          example:
            nl: Upload een kopie van uw kentekenbewijs voor uw parkeervergunningsaanvraag.
            en: Upload a copy of your vehicle registration document for your parking permit application.
        status:
          type: string
          description: "[Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112)."
          examples:
            - open
            - afgerond
            - geannuleerd
          x-enumDescriptions:
            open: Taak staat open en kan door de gebruiker worden uitgevoerd.
            afgerond: Taak is succesvol afgerond.
            geannuleerd: Taak is geannuleerd en niet meer uitvoerbaar.
        deadline:
          type: string
          format: date-time
          description: |
            **Laatste moment waarop de taak nog op tijd is** (RFC 3339 / ISO 8601, inclusief
            tijdzone-offset). Dit is een **inclusieve** bovengrens: zolang
            `nu <= deadline` is de taak op tijd; vanaf het eerstvolgende moment is hij te laat.

            De referentietijdzone is `Europe/Amsterdam` (Nederlandse lokale tijd:
            `+01:00` in de winter, `+02:00` in de zomer).

            **Codering — als een exact tijdstip bekend is:** gebruik dat tijdstip
            rechtstreeks. Voorbeeld: deadline "uiterlijk 12 december 12:00" →
            `2026-12-12T12:00:00+01:00`.

            **Codering — als alleen een kalenderdag bekend is:** gebruik conventioneel
            het einde van die dag in lokale tijd: `23:59:59`. Voorbeeld: deadline
            "uiterlijk 12 december" → `2026-12-12T23:59:59+01:00`. Zo blijft de
            getoonde datum gelijk aan wat de gebruiker leest, en is de hele dag
            nog geldig. Deze conventie wordt in de praktijk breed toegepast
            (denk aan belastingdiensten, vergunningstermijnen, formulierdeadlines)
            en sluit daarmee aan bij wat gebruikers gewend zijn.
          example: "2026-12-12T23:59:59+01:00"
        context:
          $ref: "#/components/schemas/ContextLink"
        uitvoering:
          $ref: "#/components/schemas/UitvoeringInfo"

    Taak:
      x-tags:
        - Taken
      allOf:
        - $ref: "#/components/schemas/TaakInfo"
        - type: object
          description: |
            Taakrepresentatie zoals teruggegeven door `GET /taken/{uuid}`.
            Bevat alle informatie die nodig is om een taak uit te voeren met zo min mogelijk portaalwissels,
            inclusief type-specifieke uitvoeringsvelden.
          properties:
            uitvoering:
              $ref: "#/components/schemas/Uitvoering"

    Probleem:
      x-tags:
        - Schema's
      type: object
      description: Probleemdetails conform [RFC 9457 Problem Details for HTTP APIs](https://tools.ietf.org/html/rfc9457).
      properties:
        type:
          type: string
          format: uri-reference
          description: URI-referentie die het probleemtype identificeert. Als afwezig, wordt `/problemen/onbekend` aangenomen.
          example: /problemen/ongeldige-invoer
        title:
          type: string
          description: Korte, voor mensen leesbare samenvatting van het probleemtype.
          example: Ongeldige invoer
        status:
          type: integer
          format: int32
          description: HTTP-statuscode voor dit voorval.
          example: 400
        detail:
          type: string
          description: Voor mensen leesbare uitleg van dit specifieke voorval.
          example: Het veld 'klantId' is verplicht.
        instance:
          type: string
          format: uri-reference
          description: URI-referentie die dit specifieke voorval identificeert.

  headers:
    API-Version:
      description: Versie van de API die de respons heeft gegenereerd.
      schema:
        type: string
        example: 0.1.0

  responses:
    BadRequest:
      description: Ongeldige aanvraag
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Probleem"
    Unauthorized:
      description: Authenticatie vereist
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Probleem"
    NotFound:
      description: Taak niet gevonden
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Probleem"

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - bearerAuth: []
