← Production reference

Isolated test contract · 2026-10-15-test-github-archives-release

ERiC und ELSTER test API

Persistierte deterministische Simulation mit festen Testfällen. Keine native ERiC-Ausführung, ELSTER-Übermittlung, Zertifikate oder steuerliche Nachweise.

Current service status ↗

Versioned guides and examples

Published local documents for revision 2026-10-15-test-github-archives-release. SDK guidance describes integration options; no external SDK download is implied.

Overview · overview.md
Download overview.md
# ERiC und ELSTER — isolierter Test

Produkt- und Mandantenrechte werden pro Client gebunden. Das isolierte Testprofil bietet nur zwei deterministische Simulationsoperationen und keine steuerliche Übermittlung.

Quelle: devacc-eric-svc Commit 15215cc6dc683005868d1f0f0e61e5de7408ab03, api/customer-test-openapi.yaml. Diese Dokumentation und der gefilterte OpenAPI-Vertrag sind unveränderliche Snapshots. Quellqualifikation bedeutet keine bestätigte Live-Verfügbarkeit.

Die jeweils andere Umgebung benötigt eigene Clients, Grants, Daten und Signaturschlüssel. OAuth-Issuer: https://auth.d0.eu; Ressource: https://api.test.d0.eu/eric. Maschine: Client Credentials mit private_key_jwt und DPoP. Signaturschlüssel bleiben ausschließlich im Serverprozess.

Der isolierte Testvertrag enthält ausschließlich zwei deterministische Simulationsoperationen. Der E-Bilanz-Formplan der Produktion ist hier nicht verfügbar.
Quickstart · quickstart.md
Download quickstart.md
# ERiC und ELSTER: Test in vier Schritten

1. Produkt und Organisationsberechtigung in der Console prüfen. Für Test einen eigenen Maschinenclient in d0-auth registrieren; nur die unten genannten Scopes freigeben.
2. Über `https://auth.d0.eu` ein Access Token für `https://api.test.d0.eu/eric` mit `private_key_jwt` und DPoP beziehen. Den DPoP-Proof an genau den Ziel-Request binden; Nonce-Antworten gemäß Auth-Transport behandeln.
3. Mit eric.validate POST /eric/v1/tenants/{tenantId}/products/{productId}/simulations und Fixture validation-accepted-v1 oder validation-rejected-v1 starten; mit eric.read dieselbe Simulation abrufen. tenantId muss dem X-ERIC-Tenant entsprechen. Ergebnis simulated:true ist kein ELSTER-Nachweis.
4. Nur die im `public-openapi.json` dieser Revision gelisteten Operationen verwenden. Testdaten und Test-Clients nie mit Produktion mischen.

Verfügbare Test-Scopes: `eric.read`, `eric.validate`. Pro Client nur den tatsächlich benötigten Teil beantragen.

Go v0.1.2 ist über den öffentlichen Go-Modul-Proxy installierbar. TypeScript und Python v0.1.2 samt gemeinsamem Auth-Transport sind als geprüfte Archive im [GitHub-Release](https://github.com/dev-null-GmbH/d0-sdk/releases/tag/sdk-2026-10-15) verfügbar; die genauen Installationsschritte stehen in den SDK-Guides. Die drei Beispiele sind Offline-Fixtures ohne Live-Credentials oder Live-Requests.
Examples · examples/typescript.mjs
Download examples/typescript.mjs
import { EricClient } from "../dist/index.js";
const tenant = "11111111-1111-4111-8111-111111111111", product = "22222222-2222-4222-8222-222222222222";
const transport = {async request(url, options) {
  const path = new URL(url).pathname;
  if (path.endsWith("/notices")) return new Response(JSON.stringify({schema_version:"eric.user-notices.v1",version:"fixture",sdk_release:"fixture",documentation_archive_sha256:"0".repeat(64),license_document_sha256:"0".repeat(64),license_member:"license",notices:[]}),{status:200,headers:{"Content-Type":"application/json"}});
  if (path.endsWith("/simulations")) {const fixture=JSON.parse(options.body).fixture; return new Response(JSON.stringify({id:"33333333-3333-4333-8333-333333333333",fixture,status:"completed",outcome:fixture === "validation-accepted-v1" ? "simulated_accepted" : "simulated_rejected",simulated:true}),{status:202,headers:{"Content-Type":"application/json"}});}
  throw new Error(`unexpected offline route ${path}`);
}};
const prod=new EricClient("production",tenant,transport);
const notices=await prod.GetUserNotices({productId:product});
if(notices.schema_version!=="eric.user-notices.v1")throw new Error("notice fixture failed");
const test=new EricClient("test",tenant,transport);
for(const fixture of ["validation-accepted-v1","validation-rejected-v1"]){const result=await test.createEricTestSimulation({productId:product,idempotencyKey:`offline-${fixture}`,body:{fixture}});if(!result.simulated)throw new Error("simulation fixture failed");}
console.log("ERiC production read-only + both test simulations: offline fixtures passed");
Examples · examples/go.go
Download examples/go.go
// Offline ERiC example: production notice read and both test simulations.
package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"strings"

	eric "github.com/dev-null-GmbH/d0-sdk/eric/go"
	"github.com/dev-null-GmbH/d0-sdk/eric/go/genprod"
	"github.com/google/uuid"
)

const tenant = "11111111-1111-4111-8111-111111111111"
const product = "22222222-2222-4222-8222-222222222222"

type fixtureTransport struct{}

func (fixtureTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	var code = 200
	var body string
	switch {
	case strings.HasSuffix(r.URL.Path, "/notices"):
		body = `{"schema_version":"eric.user-notices.v1","version":"fixture","sdk_release":"fixture","documentation_archive_sha256":"digest","license_document_sha256":"digest","license_member":"license","notices":[]}`
	case strings.HasSuffix(r.URL.Path, "/simulations"):
		code = 202
		b, err := io.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
		fixture := "validation-accepted-v1"
		if bytes.Contains(b, []byte("validation-rejected-v1")) {
			fixture = "validation-rejected-v1"
		}
		outcome := "simulated_accepted"
		if fixture == "validation-rejected-v1" {
			outcome = "simulated_rejected"
		}
		body = fmt.Sprintf(`{"id":"33333333-3333-4333-8333-333333333333","fixture":%q,"status":"completed","outcome":%q,"simulated":true}`, fixture, outcome)
	default:
		return nil, fmt.Errorf("unexpected offline route %s", r.URL.Path)
	}
	h := make(http.Header)
	h.Set("Content-Type", "application/json")
	return &http.Response{StatusCode: code, Header: h, Body: io.NopCloser(strings.NewReader(body))}, nil
}
func main() {
	httpClient := &http.Client{Transport: fixtureTransport{}}
	prod, err := eric.NewClient(eric.Production, tenant, httpClient)
	if err != nil {
		panic(err)
	}
	notice, err := prod.GetUserNotices(context.Background(), genprod.ID(product), &genprod.GetUserNoticesParams{})
	if err != nil || notice.JSON200 == nil {
		panic(fmt.Sprint("notice fixture: ", err))
	}
	test, err := eric.NewClient(eric.Test, tenant, httpClient)
	if err != nil {
		panic(err)
	}
	for _, fixture := range []eric.SimulationFixture{eric.ValidationAccepted, eric.ValidationRejected} {
		result, err := test.Simulate(context.Background(), uuid.MustParse(product), fixture, "offline-eric-001")
		if err != nil || !result.Simulated {
			panic(fmt.Sprint("simulation fixture: ", err))
		}
	}
	fmt.Println("ERiC production read-only + both test simulations: offline fixtures passed")
}
Examples · examples/python.py
Download examples/python.py
"""Offline production notice and accepted/rejected test simulation examples."""
import io
import json
from email.message import Message
from typing import Any

from d0_auth_machine import MachineClient

from d0_eric import EricClient, resource_for

TENANT = "11111111-1111-4111-8111-111111111111"
PRODUCT = "22222222-2222-4222-8222-222222222222"

class Response(io.BytesIO):
    def __init__(self, code: int, value: dict[str, Any]):
        super().__init__(json.dumps(value).encode())
        self.status = code
        self.headers = Message()
        self.headers["Content-Type"] = "application/json"

class FixtureMachine(MachineClient):
    def __init__(self, profile: str):
        self.resource = resource_for(profile)  # type: ignore[arg-type]

    def request(self, url: str, *, method: str = "GET", headers: Any = None, body: Any = None, cancel: Any = None, timeout: float = 45.0) -> Any:
        assert headers["X-ERIC-Tenant"] == TENANT
        if url.endswith("/notices"):
            return Response(200, {"schema_version": "eric.user-notices.v1", "version": "fixture", "sdk_release": "fixture", "documentation_archive_sha256": "0"*64, "license_document_sha256": "0"*64, "license_member": "license", "notices": []})
        if url.endswith("/simulations"):
            fixture = json.loads(body)["fixture"]
            return Response(202, {"id": "33333333-3333-4333-8333-333333333333", "fixture": fixture, "status": "completed", "outcome": "simulated_accepted" if fixture == "validation-accepted-v1" else "simulated_rejected", "simulated": True})
        raise AssertionError(url)

prod = EricClient("production", TENANT, FixtureMachine("production"))  # type: ignore[arg-type]
assert prod.get_user_notices({"productId": PRODUCT})["schema_version"] == "eric.user-notices.v1"
test = EricClient("test", TENANT, FixtureMachine("test"))  # type: ignore[arg-type]
for fixture, outcome in (("validation-accepted-v1", "simulated_accepted"), ("validation-rejected-v1", "simulated_rejected")):
    result = test.create_eric_test_simulation({"productId": PRODUCT, "idempotencyKey": "offline-eric-001", "body": {"fixture": fixture}})  # type: ignore[typeddict-item]
    assert result["outcome"] == outcome
print("ERiC production read-only + both test simulations: offline fixtures passed")
Error catalog · errors.md
Download errors.md
# ERiC und ELSTER — Fehler und Wiederholung

Der genaue Fehlerkörper und die operationellen Antworten stehen in `public-openapi.json`. In den öffentlichen Operationen dieser Revision sind die Statuscodes `200`, `202`, `default` deklariert. Nicht jeder Code gilt für jede Operation.

- `400`/`422`: Eingabe und geforderte Header gegen die konkrete Operation prüfen; keinen veränderten Request unter demselben Idempotency-Key wiederholen.
- `401`: Token, DPoP-Proof, Nonce, Audience und Clientprofil prüfen.
- `403`: Scope, Organisationsrolle und fachliche Zuordnung prüfen; keine breiteren Scopes erraten.
- `404`: Pfad, Ressource und Tenant-Kontext prüfen; keine Existenz fremder Ressourcen ableiten.
- `409`/`412`: Konflikt bzw. ETag-Vorbedingung durch erneutes Lesen und fachliche Entscheidung auflösen.
- `429`: technische Limitantwort und Reset-/Retry-Hinweis beachten.
- `5xx` oder Transportabbruch: Bei Mutationen denselben vom Aufrufer vergebenen Idempotency-Key verwenden, sofern die Operation ihn vorsieht; sonst erst den serverseitigen Zustand lesen. Keine blinde Wiederholung einer nicht-idempotenten Operation.

Problemantworten und Request-ID helfen bei der Diagnose. Keine Tokens, Schlüssel, Dokumentinhalte oder personenbezogenen Daten in Support-Logs kopieren.
Changelog · CHANGELOG.md
Download CHANGELOG.md
# ERiC und ELSTER 2026-10-15-test-github-archives-release

- Neue unveränderliche Dokumentationsrevision. Die vorige Revision `2026-10-15-test-filing-consent-go-012-release` bleibt bytegleich verfügbar; Quell-OpenAPI, öffentlicher Vertrag und klassifizierte Operationen sind unverändert.
- Das [GitHub-Release](https://github.com/dev-null-GmbH/d0-sdk/releases/tag/sdk-2026-10-15) stellt die geprüften TypeScript-Tarballs und Python-Wheels für eric v0.1.2 sowie den Auth-Transport v0.1.0 bereit. Der ZIP-Hash ist `6c11b63a5ce5f24dff79d1f3d33c7879ff48831d66dd9029b37abf073152dd93`; der innere `SHA256SUMS`-Katalog wird vor Installation geprüft. Go v0.1.2 bleibt über den öffentlichen Modul-Proxy verfügbar.
- npm und PyPI sind keine Voraussetzungen für diese Distribution. Die installierten Offline-Beispiele wurden im Release-Build geprüft; Service-Rollout und SDK-Download sind getrennte Nachweise.
SDK guidance · sdk-typescript.md
Download sdk-typescript.md
# ERiC und ELSTER typescript SDK

Paket: `@dev-null-gmbh/eric` v0.1.2, Auth-Transport `@dev-null-gmbh/auth-machine` v0.1.0. MIT-Lizenz; Node.js 24.21 bis unter 27.

Die [qualifizierte GitHub-Release-Datei](https://github.com/dev-null-GmbH/d0-sdk/releases/tag/sdk-2026-10-15) ist die direkte Distribution. Archiv und Prüfsumme herunterladen, dann äußere und innere SHA-256-Werte prüfen (unter Linux kann `sha256sum -c` statt `shasum -a 256 -c` verwendet werden):

```sh
curl -fLO https://github.com/dev-null-GmbH/d0-sdk/releases/download/sdk-2026-10-15/d0-sdk-2026-10-15.zip
curl -fLO https://github.com/dev-null-GmbH/d0-sdk/releases/download/sdk-2026-10-15/d0-sdk-2026-10-15.sha256
shasum -a 256 -c d0-sdk-2026-10-15.sha256
unzip d0-sdk-2026-10-15.zip -d d0-sdk-2026-10-15
(cd d0-sdk-2026-10-15 && shasum -a 256 -c SHA256SUMS)
```

Der äußere ZIP-Hash lautet `6c11b63a5ce5f24dff79d1f3d33c7879ff48831d66dd9029b37abf073152dd93`. Die Archive stammen aus [dem erfolgreichen Main-Build](https://github.com/dev-null-GmbH/d0-sdk/actions/runs/38748901791) bei `d0-sdk@6f2c39612a661ec557e96e5caff093a057e6e1de`. Das Release enthält SDKs und Auth-Transport, keine privaten Services oder Zugangsdaten.

Die beiden geprüften Tarballs lokal im Serverprojekt installieren (keine npm-Registry-Veröffentlichung erforderlich):

```sh
npm install --ignore-scripts --no-audit --no-fund ./d0-sdk-2026-10-15/npm/dev-null-gmbh-auth-machine-0.1.0.tgz ./d0-sdk-2026-10-15/npm/dev-null-gmbh-eric-0.1.2.tgz
```

Das Offline-Beispiel dieser Revision liegt unter `catalog/v1/bundles/eric/2026-10-15-test-github-archives-release/examples/typescript.mjs`. Das Paket ist für vertrauenswürdige Serveranwendungen gedacht; private Signaturschlüssel gehören nicht in Browser-Code. Öffentliche Operationen und Scopes stehen in `public-openapi.json`.
SDK guidance · sdk-go.md
Download sdk-go.md
# ERiC und ELSTER Go SDK

Paket: `github.com/dev-null-GmbH/d0-sdk/eric/go` v0.1.2. Gemeinsamer Maschinen-Auth-Transport: `github.com/dev-null-GmbH/d0-sdk/auth/go` v0.1.0. Beide wurden über den öffentlichen Go-Modul-Proxy mit aktiver Checksum-Datenbank aus frischer, anonymer Umgebung installiert und geprüft. Lizenz: MIT.

```sh
go get github.com/dev-null-GmbH/d0-sdk/eric/go@v0.1.2
```

Modul-Tag: `eric/go/v0.1.2` auf `d0-sdk@6f2c39612a661ec557e96e5caff093a057e6e1de`; Auth-Tag: `auth/go/v0.1.0` (unverändert). Der API-Quellvertrag ist `devacc-eric-svc@15215cc6dc683005868d1f0f0e61e5de7408ab03:api/customer-test-openapi.yaml`. Go v0.1.1 bleibt unter seinem eigenen Tag als historische Version installierbar.

Das unveränderte Offline-Fixture aus `devacc-eric-svc@15215cc6dc683005868d1f0f0e61e5de7408ab03:sdk/go/examples/fixture/main.go` liegt unter `catalog/v1/bundles/eric/2026-10-15-test-github-archives-release/examples/go.go` (SHA-256 `820c9073c7292b8e6d753f9cc80b32979a0553758ba4fa69038597bfa70c2289`). Es demonstriert den Client mit festem test-Profil ohne Netzaufruf. Öffentliche Operationen und Scopes stehen ausschließlich in public-openapi.json.

Die übrigen Sprachpakete sind über das [geprüfte GitHub-Release](https://github.com/dev-null-GmbH/d0-sdk/releases/tag/sdk-2026-10-15) verfügbar. Go benötigt nur den oben gezeigten Modulbezug über den öffentlichen Proxy.
SDK guidance · sdk-python.md
Download sdk-python.md
# ERiC und ELSTER python SDK

Paket: `d0-eric` v0.1.2, Auth-Transport `d0-auth-machine` v0.1.0. MIT-Lizenz; Python 3.14.

Die [qualifizierte GitHub-Release-Datei](https://github.com/dev-null-GmbH/d0-sdk/releases/tag/sdk-2026-10-15) ist die direkte Distribution. Archiv und Prüfsumme herunterladen, dann äußere und innere SHA-256-Werte prüfen (unter Linux kann `sha256sum -c` statt `shasum -a 256 -c` verwendet werden):

```sh
curl -fLO https://github.com/dev-null-GmbH/d0-sdk/releases/download/sdk-2026-10-15/d0-sdk-2026-10-15.zip
curl -fLO https://github.com/dev-null-GmbH/d0-sdk/releases/download/sdk-2026-10-15/d0-sdk-2026-10-15.sha256
shasum -a 256 -c d0-sdk-2026-10-15.sha256
unzip d0-sdk-2026-10-15.zip -d d0-sdk-2026-10-15
(cd d0-sdk-2026-10-15 && shasum -a 256 -c SHA256SUMS)
```

Der äußere ZIP-Hash lautet `6c11b63a5ce5f24dff79d1f3d33c7879ff48831d66dd9029b37abf073152dd93`. Die Archive stammen aus [dem erfolgreichen Main-Build](https://github.com/dev-null-GmbH/d0-sdk/actions/runs/38748901791) bei `d0-sdk@6f2c39612a661ec557e96e5caff093a057e6e1de`. Das Release enthält SDKs und Auth-Transport, keine privaten Services oder Zugangsdaten.

Die beiden geprüften Wheels in einer virtuellen Umgebung installieren (keine PyPI-Veröffentlichung erforderlich):

```sh
python3.14 -m venv .venv
.venv/bin/python -m pip install ./d0-sdk-2026-10-15/python/d0_auth_machine-0.1.0-py3-none-any.whl ./d0-sdk-2026-10-15/python/d0_eric-0.1.2-py3-none-any.whl
```

Das Offline-Beispiel dieser Revision liegt unter `catalog/v1/bundles/eric/2026-10-15-test-github-archives-release/examples/python.py`. Das Paket ist für vertrauenswürdige Serveranwendungen gedacht; private Signaturschlüssel gehören nicht in Browser-Code. Öffentliche Operationen und Scopes stehen in `public-openapi.json`.

Test workflow

Use a dedicated test machine client for published API operations or a dedicated human session for the browser Explorer. Production credentials are not accepted.

Open Test Explorer →

Published operations

Only public, implemented operations in this pinned bundle are actionable. Machine DPoP and human Bearer requirements remain separate.

MethodPathOperationReadiness
POST/v1/tenants/{tenantId}/products/{productId}/simulationscreateEricTestSimulationimplemented
GET/v1/tenants/{tenantId}/products/{productId}/simulations/{simulationId}getEricTestSimulationimplemented

Operation reference

2 of 2 operations

POST /v1/tenants/{tenantId}/products/{productId}/simulationscreateEricTestSimulation

Parameters

tenantId · path · required

{
  "type": "string",
  "format": "uuid"
}

productId · path · required

{
  "type": "string",
  "format": "uuid"
}

X-ERIC-Tenant · header · required

{
  "type": "string",
  "format": "uuid"
}

Idempotency-Key · header · required

{
  "type": "string",
  "pattern": "^[A-Za-z0-9._:-]{1,128}$"
}

Request body

application/json · required

Required fields: fixture

{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "fixture"
  ],
  "properties": {
    "fixture": {
      "enum": [
        "validation-accepted-v1",
        "validation-rejected-v1"
      ]
    }
  }
}

Responses

202 · application/json

Durable synthetic result; never native evidence

Required fields: id, fixture, status, outcome, simulated

{
  "type": "object",
  "required": [
    "id",
    "fixture",
    "status",
    "outcome",
    "simulated"
  ],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "fixture": {
      "enum": [
        "validation-accepted-v1",
        "validation-rejected-v1"
      ]
    },
    "status": {
      "const": "completed"
    },
    "outcome": {
      "enum": [
        "simulated_accepted",
        "simulated_rejected"
      ]
    },
    "simulated": {
      "const": true
    }
  }
}

default · application/problem+json

Fail-closed problem response

Required fields: type, title, status

{
  "type": "object",
  "required": [
    "type",
    "title",
    "status"
  ]
}
GET /v1/tenants/{tenantId}/products/{productId}/simulations/{simulationId}getEricTestSimulation

Parameters

tenantId · path · required

{
  "type": "string",
  "format": "uuid"
}

productId · path · required

{
  "type": "string",
  "format": "uuid"
}

X-ERIC-Tenant · header · required

{
  "type": "string",
  "format": "uuid"
}

simulationId · path · required

{
  "type": "string",
  "format": "uuid"
}

Responses

200 · application/json

Current client's durable synthetic status

Required fields: id, fixture, status, outcome, simulated

{
  "type": "object",
  "required": [
    "id",
    "fixture",
    "status",
    "outcome",
    "simulated"
  ],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "fixture": {
      "enum": [
        "validation-accepted-v1",
        "validation-rejected-v1"
      ]
    },
    "status": {
      "const": "completed"
    },
    "outcome": {
      "enum": [
        "simulated_accepted",
        "simulated_rejected"
      ]
    },
    "simulated": {
      "const": true
    }
  }
}

default · application/problem+json

Fail-closed problem response

Required fields: type, title, status

{
  "type": "object",
  "required": [
    "type",
    "title",
    "status"
  ]
}