EDC Connector API
Getting Started

Getting Started

Quick start guide for the sovity EDC API Wrapper

Getting Started

This guide will help you make your first API call to the sovity EDC API Wrapper. The UI API provides a simplified, type-safe interface for managing your EDC Connector.

Prerequisites

Before you begin, ensure you have:

  • Access to an EDC Connector instance (provided by your Datenraum Kultur administrator)
  • An API key for authentication
  • A tool for making HTTP requests (curl, Postman, or your preferred HTTP client)

Base URL

All API requests should be made to your connector instance's Management API:

https://api.your-connector-instance.prod.truzztbox.eu/api/management

Replace this with your actual connector URL.

API Structure

The sovity UI API uses a page-based structure that provides all data needed for specific UI views:

EndpointPurpose
/wrapper/ui/pages/dashboard-pageConnector overview and statistics
/wrapper/ui/pages/asset-pageAsset management
/wrapper/ui/pages/policy-pagePolicy definitions
/wrapper/ui/pages/contract-definition-pageContract definitions
/wrapper/ui/pages/contract-agreement-pageContract agreements
/wrapper/ui/pages/catalog-page/data-offersCatalog browsing
/wrapper/ui/pages/transfer-history-pageTransfer history
/wrapper/ui/pages/create-data-offerQuick data offer creation

Your First API Call: Dashboard

Let's start by querying the Dashboard to get an overview of your connector:

curl -X GET "https://api.your-connector-instance.prod.truzztbox.eu/api/management/wrapper/ui/pages/dashboard-page" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your-api-key"
const BASE_URL = 'https://api.your-connector-instance.prod.truzztbox.eu/api/management';

const response = await fetch(`${BASE_URL}/wrapper/ui/pages/dashboard-page`, {
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': 'your-api-key',
  },
});

const dashboard = await response.json();
console.log('Connector:', dashboard.connectorTitle);
console.log('Assets:', dashboard.numAssets);
console.log('Policies:', dashboard.numPolicies);
console.log('Contract Definitions:', dashboard.numContractDefinitions);

Dashboard Response

{
  "numAssets": 5,
  "numPolicies": 3,
  "numContractDefinitions": 4,
  "numContractAgreementsConsuming": 2,
  "numContractAgreementsProviding": 7,
  "transferProcessesConsuming": {
    "numTotal": 10,
    "numRunning": 1,
    "numOk": 8,
    "numError": 1
  },
  "transferProcessesProviding": {
    "numTotal": 15,
    "numRunning": 0,
    "numOk": 14,
    "numError": 1
  },
  "connectorEndpoint": "https://your-connector.truzztbox.eu/protocol",
  "connectorParticipantId": "BPNL000000000001",
  "connectorTitle": "Datenraum Kultur Connector",
  "connectorDescription": "EDC Connector for cultural data exchange",
  "connectorCuratorName": "Your Organization",
  "connectorCuratorUrl": "https://your-organization.de",
  "managementApiUrlShownInDashboard": "https://api.your-connector-instance.prod.truzztbox.eu/api/management"
}

Quick Data Offer Creation

The fastest way to publish data is using the Quick Data Offer endpoint. This creates an asset, policy, and contract definition in one call.

This endpoint uses a flat structure where all asset fields are at the root level, not nested inside an asset object.

curl -X POST "https://api.your-connector-instance.prod.truzztbox.eu/api/management/wrapper/ui/pages/create-data-offer" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your-api-key" \
  -d '{
    "id": "museum-collection-2024",
    "title": "Museum Collection Metadata 2024",
    "description": "Comprehensive metadata for our 2024 digital collection",
    "language": "https://w3id.org/idsa/code/DE",
    "version": "1.0",
    "keywords": ["museum", "cultural-heritage", "metadata"],
    "mediaType": "application/json",
    "publisherHomepage": "",
    "licenseUrl": "",
    "landingPageUrl": "",
    "drkAssetType": "MUSEUM",
    "drkMuseumName": "Hamburger Kunsthalle",
    "dataSource": {
      "type": "HTTP_DATA",
      "httpData": {
        "method": "GET",
        "baseUrl": "https://api.museum.example.com/collection/2024",
        "queryString": "",
        "authHeaderValue": {},
        "headers": {},
        "enableMethodParameterization": false,
        "enablePathParameterization": false,
        "enableQueryParameterization": false,
        "enableBodyParameterization": false
      }
    },
    "publishType": "PUBLISH_UNRESTRICTED"
  }'
type DrkAssetType = 'GENERAL' | 'MUSEUM' | 'THEATRE_PLAN' | 'MUSICSCHOOL';

interface DataOfferCreateRequest {
  // Required
  id: string;
  dataSource: {
    type: 'HTTP_DATA' | 'ON_REQUEST' | 'CUSTOM';
    httpData?: {
      method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
      baseUrl: string;
      queryString?: string;
      authHeaderValue?: Record<string, string>;
      headers?: Record<string, string>;
      enableMethodParameterization?: boolean;
      enablePathParameterization?: boolean;
      enableQueryParameterization?: boolean;
      enableBodyParameterization?: boolean;
    };
    onRequest?: {
      contactEmail: string;
      contactPreferredEmailSubject: string;
    };
  };
  
  // Optional metadata
  title?: string;
  description?: string;
  language?: string;                    // IDS URI, e.g., "https://w3id.org/idsa/code/DE"
  version?: string;
  keywords?: string[];
  mediaType?: string;
  publisherHomepage?: string;
  licenseUrl?: string;
  landingPageUrl?: string;              // Link to asset documentation
  
  // DRK-specific (all optional, default drkAssetType = 'GENERAL')
  drkAssetType?: DrkAssetType;
  drkMuseumName?: string | null;        // When drkAssetType = 'MUSEUM'
  drkTheatreName?: string | null;       // When drkAssetType = 'THEATRE_PLAN'
  drkTheatreStreetaddress?: string | null;
  drkTheatrePostalCode?: string | null;
  drkTheatreLocality?: string | null;
  drkTheatreCountry?: string | null;
  drkMusicSchoolName?: string | null;   // When drkAssetType = 'MUSICSCHOOL'
  
  // Policy (optional)
  publishType?: 'DONT_PUBLISH' | 'PUBLISH_UNRESTRICTED' | 'PUBLISH_RESTRICTED';
  policyExpression?: UiPolicyExpression;
}

const request: DataOfferCreateRequest = {
  id: 'museum-collection-2024',
  title: 'Museum Collection Metadata 2024',
  description: 'Comprehensive metadata for our 2024 digital collection',
  language: 'https://w3id.org/idsa/code/DE',
  version: '1.0',
  keywords: ['museum', 'cultural-heritage', 'metadata'],
  mediaType: 'application/json',
  landingPageUrl: 'https://museum.example.com/api-docs',
  drkAssetType: 'MUSEUM',
  drkMuseumName: 'Hamburger Kunsthalle',
  dataSource: {
    type: 'HTTP_DATA',
    httpData: {
      method: 'GET',
      baseUrl: 'https://api.museum.example.com/collection/2024',
    },
  },
  publishType: 'PUBLISH_UNRESTRICTED',
};

const response = await fetch(
  `${BASE_URL}/wrapper/ui/pages/create-data-offer`,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'your-api-key',
    },
    body: JSON.stringify(request),
  }
);

const result = await response.json();
console.log('Created data offer with ID:', result.id);

Publish Types

TypeDescription
DONT_PUBLISHCreate asset only, no contract definition
PUBLISH_UNRESTRICTEDCreate with always-true policy (anyone can access)
PUBLISH_RESTRICTEDCreate with custom policy expression

All publish types are optional. If not specified, only the asset is created.

DRK Asset Types

Assets can be categorized by cultural domain using drkAssetType:

Asset TypeDescriptionConditional Fields
GENERAL (default)General purpose data assetNone
MUSEUMMuseum-related datadrkMuseumName
THEATRE_PLANTheatre schedule datadrkTheatreName, drkTheatreStreetaddress, drkTheatrePostalCode, drkTheatreLocality, drkTheatreCountry
MUSICSCHOOLMusic school datadrkMusicSchoolName

Theatre Example

const theatreOffer: DataOfferCreateRequest = {
  id: 'theatre-schedule-2024',
  title: 'Theatre Performance Schedule 2024',
  description: 'Machine-readable theatre schedule data',
  language: 'https://w3id.org/idsa/code/DE',
  version: '1.0',
  keywords: ['theatre', 'schedule', 'performances'],
  mediaType: 'application/json',
  drkAssetType: 'THEATRE_PLAN',
  drkTheatreName: 'Deutsches Schauspielhaus',
  drkTheatreStreetaddress: 'Kirchenallee 39',
  drkTheatrePostalCode: '20099',
  drkTheatreLocality: 'Hamburg',
  drkTheatreCountry: 'Germany',
  dataSource: {
    type: 'HTTP_DATA',
    httpData: {
      method: 'GET',
      baseUrl: 'https://api.theatre.example.com/schedule',
    },
  },
  publishType: 'PUBLISH_UNRESTRICTED',
};

Restricted Publishing Example

const restrictedOffer: DataOfferCreateRequest = {
  id: 'research-dataset-2024',
  title: 'Research Dataset 2024',
  language: 'https://w3id.org/idsa/code/EN',
  drkAssetType: 'GENERAL',
  dataSource: {
    type: 'HTTP_DATA',
    httpData: {
      method: 'GET',
      baseUrl: 'https://api.research.example.com/dataset',
    },
  },
  publishType: 'PUBLISH_RESTRICTED',
  policyExpression: {
    type: 'AND',
    expressions: [
      {
        type: 'CONSTRAINT',
        constraint: {
          left: 'purpose',
          operator: 'EQ',
          right: { type: 'STRING', value: 'research' },
        },
      },
      {
        type: 'CONSTRAINT',
        constraint: {
          left: 'participantType',
          operator: 'IN',
          right: {
            type: 'STRING_LIST',
            valueList: ['university', 'research-institute'],
          },
        },
      },
    ],
  },
};

List Your Assets

Retrieve all assets in your connector:

curl -X GET "https://api.your-connector-instance.prod.truzztbox.eu/api/management/wrapper/ui/pages/asset-page" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your-api-key"
interface UiAsset {
  assetId: string;
  title: string;
  description?: string;
  dataSourceAvailability: 'LIVE' | 'ON_REQUEST';
  creatorOrganizationName: string;
  keywords?: string[];
  // ... more fields
}

interface AssetPage {
  assets: UiAsset[];
}

const response = await fetch(`${BASE_URL}/wrapper/ui/pages/asset-page`, {
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': 'your-api-key',
  },
});

const page: AssetPage = await response.json();
console.log('Total assets:', page.assets.length);
page.assets.forEach((asset) => {
  console.log(`- ${asset.assetId}: ${asset.title}`);
});

Browse a Catalog

Discover data offers from other connectors:

curl -X GET "https://api.your-connector-instance.prod.truzztbox.eu/api/management/wrapper/ui/pages/catalog-page/data-offers?connectorEndpoint=https://other-connector.truzztbox.eu/protocol" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your-api-key"
interface UiDataOffer {
  endpoint: string;
  participantId: string;
  asset: UiAsset;
  contractOffers: Array<{
    contractOfferId: string;
    policy: UiPolicy;
  }>;
}

async function browseCatalog(connectorEndpoint: string): Promise<UiDataOffer[]> {
  const params = new URLSearchParams({ connectorEndpoint });
  const response = await fetch(
    `${BASE_URL}/wrapper/ui/pages/catalog-page/data-offers?${params}`,
    {
      headers: {
        'Content-Type': 'application/json',
        'X-Api-Key': 'your-api-key',
      },
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to fetch catalog: ${response.statusText}`);
  }

  return response.json();
}

const offers = await browseCatalog('https://other-connector.truzztbox.eu/protocol');
console.log('Available data offers:', offers.length);
offers.forEach((offer) => {
  console.log(`- ${offer.asset.title} (${offer.contractOffers.length} contract offers)`);
});

Complete Workflow Example

Check Connector Status

Verify your connector is operational:

const dashboard = await fetch(`${BASE_URL}/wrapper/ui/pages/dashboard-page`, {
  headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'your-api-key' },
}).then((r) => r.json());

console.log('Connector:', dashboard.connectorTitle);
console.log('Participant ID:', dashboard.connectorParticipantId);

Create a Data Offer

Publish your first dataset:

const offer = await fetch(`${BASE_URL}/wrapper/ui/pages/create-data-offer`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'your-api-key' },
  body: JSON.stringify({
    id: 'my-first-dataset',
    title: 'My First Dataset',
    language: 'https://w3id.org/idsa/code/DE',
    drkAssetType: 'GENERAL',
    dataSource: {
      type: 'HTTP_DATA',
      httpData: { 
        method: 'GET',
        baseUrl: 'https://my-api.example.com/data' 
      },
    },
    publishType: 'PUBLISH_UNRESTRICTED',
  }),
}).then((r) => r.json());

console.log('Created:', offer.id);

Verify in Asset Page

Confirm the asset was created:

const assets = await fetch(`${BASE_URL}/wrapper/ui/pages/asset-page`, {
  headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'your-api-key' },
}).then((r) => r.json());

const myAsset = assets.assets.find((a) => a.assetId === 'my-first-dataset');
console.log('Asset found:', myAsset?.title);

Browse Other Catalogs

Discover data from partner connectors:

const partnerOffers = await fetch(
  `${BASE_URL}/wrapper/ui/pages/catalog-page/data-offers?connectorEndpoint=https://partner.truzztbox.eu/protocol`,
  {
    headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'your-api-key' },
  }
).then((r) => r.json());

console.log('Partner offers:', partnerOffers.length);

Key Differences from Standard EDC API

The sovity UI API differs from the standard Eclipse EDC Management API:

FeatureStandard EDC APIsovity UI API
FormatJSON-LD with @contextPlain JSON
Endpoints/v3/assets, /v3/policydefinitions/wrapper/ui/pages/...
QueryQuerySpec with filterExpressionPage-based, pre-aggregated
Data OffersSeparate asset + policy + contract definitionSingle create-data-offer endpoint
StatesRaw EDC state codesSimplified states (RUNNING, OK, ERROR)

The sovity UI API wraps the standard EDC API to provide a simpler, more type-safe interface. All operations are still EDC-compliant.

Next Steps

On this page