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
# 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
# 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
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
// 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
"""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
# 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
# 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
# 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
# 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
# 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.
| Method | Path | Operation | Readiness |
|---|---|---|---|
| POST | /v1/tenants/{tenantId}/products/{productId}/simulations | createEricTestSimulation | implemented |
| GET | /v1/tenants/{tenantId}/products/{productId}/simulations/{simulationId} | getEricTestSimulation | implemented |
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"
]
}