DCI Overview#

For: developers

Understand the Digital Convergence Initiative (DCI) architecture and how OpenSPP implements it — the three-part message envelope, HTTP Signature authentication, registry types, and interaction patterns (sync/async search, subscribe/notify).

Prerequisites#

  • Familiarity with REST APIs and JSON

  • Basic understanding of OAuth 2.0 client credentials flow

  • Optional: familiarity with HTTP Message Signatures (RFC 9421 / draft-cavage)

What is DCI?#

The Digital Convergence Initiative (DCI) is a set of open API standards designed for interoperability between social protection systems. DCI enables governments and organizations to:

  • Exchange beneficiary data between different registries

  • Avoid duplication across programs

  • Enable data portability and consent-based sharing

  • Standardize communication between CRVS, social registries, and program management systems

Why DCI Matters for OpenSPP#

OpenSPP is often deployed as part of a larger ecosystem of government systems:

System Type

Example

Integration Need

Civil Registration

National CRVS

Import birth/death records to update registry

Beneficiary Registries

National IBR

Check if person is enrolled elsewhere to prevent duplication

MIS/Dashboards

Ministry dashboard

Query OpenSPP registry for reporting and analytics

Disability Registries

National DR

Query disability status for eligibility targeting

Other Social Registries

Provincial registries

Federated beneficiary lookups

Without DCI, each integration requires custom API development. With DCI, OpenSPP uses standardized protocols that work across different systems.

DCI Architecture#

        graph TB
    subgraph "OpenSPP as DCI Server"
        A[External System] -->|DCI Search Request| B[DCI Server Module]
        B -->|Query| C[OpenSPP Registry]
        C -->|Results| B
        B -->|DCI Response| A
    end

    subgraph "OpenSPP as DCI Client"
        D[DCI Client Module] -->|DCI Search Request| E[External Registry]
        E -->|Results| D
        D -->|Import| F[OpenSPP Registry]
    end
    

Key Concepts#

Three-Part Message Envelope#

Every DCI message has three parts:

{
  "signature": "Signature: namespace=\"dci\", kidId=\"...\", ...",
  "header": {
    "version": "1.0.0",
    "message_id": "uuid",
    "action": "search",
    "sender_id": "openspp.example.org",
    "receiver_id": "crvs.national.gov"
  },
  "message": {
    "transaction_id": "uuid",
    "search_request": [...]
  }
}
  • Signature - HTTP Signature for message authenticity (Ed25519 or RSA)

  • Header - Metadata about the message (sender, receiver, action)

  • Message - The actual request/response payload

Registry Types#

DCI defines standard registry types:

Registry Type

Code

OpenSPP Role

Purpose

Social Registry

SOCIAL_REGISTRY

Server

Expose beneficiary/household data

CRVS

CRVS

Client

Import birth/death events

IBR

IBR

Client

Check enrollments in other programs

Disability Registry

DR

Client

Query disability status

Farmer Registry

FR

Server

Expose farmer registrants

Interaction Patterns#

Synchronous Search:

        sequenceDiagram
    Client->>Server: POST /registry/sync/search
    Note over Server: Process immediately
    Server-->>Client: 200 OK with results
    

Asynchronous Search:

        sequenceDiagram
    Client->>Server: POST /registry/search (with callback_uri)
    Server-->>Client: 202 Accepted
    Note over Server: Process in background via queue_job
    Server->>Client: POST {callback_uri}/on-search
    Client-->>Server: 200 OK
    

Subscribe/Notify:

        sequenceDiagram
    Subscriber->>Registry: POST /registry/subscribe
    Registry-->>Subscriber: 202 Accepted
    Note over Registry: Event occurs (e.g., new registration)
    Registry->>Subscriber: POST {callback_uri}/notify
    Subscriber-->>Registry: 200 OK
    

OpenSPP DCI Architecture#

Module Structure#

spp_dci/                      # Core infrastructure
├── models/
│   ├── dci_envelope.py       # Message envelope schemas
│   └── dci_signing_key.py    # Key management
└── services/
    ├── signature.py          # HTTP signature creation/verification
    └── mapper.py             # DCI ↔ OpenSPP data mapping

spp_dci_server/               # Server implementation
├── routers/
│   ├── auth.py               # OAuth2 token endpoint
│   ├── registry.py           # Search/subscribe endpoints
│   └── wellknown.py          # JWKS and metadata
└── services/
    ├── search_service.py     # Search logic
    └── subscription_service.py # Event subscriptions

spp_dci_client/               # Client implementation
├── models/
│   └── dci_data_source.py    # External registry config
└── services/
    └── dci_client.py         # Generic DCI client

spp_dci_client_crvs/          # CRVS-specific client
spp_dci_client_ibr/           # IBR-specific client
spp_dci_client_dr/            # DR-specific client

Integration with Existing Infrastructure#

DCI builds on OpenSPP's existing API V2 infrastructure:

Component

Reused From

Purpose

Authentication

spp_oauth

OAuth2 client credentials flow

External IDs

spp_api_v2

UUID-based identifiers for data exchange

Consent Management

spp_consent

Privacy-preserving data sharing

Background Jobs

queue_job

Async request processing

FastAPI

fastapi OCA module

REST endpoint framework

Use Cases#

Server Use Cases#

1. MIS Reporting Dashboard

A national MIS needs to query OpenSPP for beneficiary counts by region:

# External system queries OpenSPP
response = await dci_client.search_by_expression(
    conditions=[
        {"attribute": "is_registrant", "operator": "=", "value": True},
        {"attribute": "area_id.code", "operator": "=", "value": "REGION_01"}
    ]
)

2. Inter-Program Coordination

Another program wants to check if households are already enrolled:

# Query OpenSPP IBR server for enrollment status
response = await ibr_client.check_enrollment(
    identifier_type="urn:gov:national-id",
    identifier_value="12345678"
)

Client Use Cases#

1. CRVS Birth Verification

Verify a registrant's birth against the national CRVS before activation:

from odoo.addons.spp_dci_client_crvs.services.crvs_service import CRVSService

crvs = CRVSService(self.env, data_source_code="crvs_main")
birth = crvs.verify_birth(
    identifier_type="urn:gov:national-id",
    identifier_value=partner.registry_id_ids[0].value,
)
if birth:
    partner.write({"birth_date": birth["date"], "verified_by_crvs": True})

2. Duplication Prevention

Before enrolling in a cash transfer program, check IBR for existing enrollments:

from odoo.addons.spp_dci_client_ibr.services.ibr_service import IBRService

data_source = self.env["spp.dci.data.source"].search(
    [("code", "=", "ibr_main")], limit=1,
)
ibr = IBRService(data_source, self.env)
result = ibr.check_duplication(partner)
if result.get("is_duplicate"):
    raise UserError(_("Already enrolled in: %s") % result["programs"])

3. Disability Targeting

Query the disability registry for PWD status to determine eligibility:

from odoo.addons.spp_dci_client_dr.services.dr_service import DRService

dr = DRService(self.env, data_source_code="dr_main")
status = dr.get_disability_status(partner)
has_severe_disability = status and any(
    score >= 3 for score in status.get("functional_scores", {}).values()
)

Data Schemas#

DCI uses JSON-LD schemas for data exchange:

Person Schema#

{
  "@context": "https://schema.spdci.org/core/v1",
  "@type": "Person",
  "identifier": [
    {
      "identifier_type": "urn:gov:national-id",
      "identifier_value": "12345678"
    }
  ],
  "name": {
    "given_name": "John",
    "surname": "Doe"
  },
  "sex": "male",
  "birth_date": "1990-05-15",
  "address": [...],
  "phone_number": ["+1234567890"],
  "registration_date": "2024-01-15T10:30:00Z"
}

Group/Household Schema#

{
  "@context": "https://schema.spdci.org/core/v1",
  "@type": "Group",
  "group_identifier": [...],
  "group_type": "Household",
  "group_head_info": {...},
  "group_size": 5,
  "member_list": [...],
  "poverty_score": 0.75
}

Authentication and Security#

OpenSPP's DCI server uses two complementary authentication mechanisms on every request:

1. Bearer token (allowlist)#

Every request must carry Authorization: Bearer <token>. The server validates the token against an allowlist configured in the dci.api_tokens Odoo system parameter (comma-separated values). This is a pre-shared token scheme, not an OAuth 2.0 flow — there is no /oauth/token endpoint on the DCI server that issues tokens dynamically. Administrators rotate tokens by updating the system parameter.

2. HTTP Message Signature (draft-cavage)#

Every request must also carry a signed DCI message envelope. The signature field in the envelope is an HTTP Message Signatures–style parameter string:

namespace="dci", kidId="<sender_id>|<key_id>|<algorithm>",
algorithm="ed25519", created="<unix_ts>", expires="<unix_ts>",
headers="(created) (expires) digest", signature="<base64>"

The signing string covers (created), (expires), and the SHA-256 digest of the compact-JSON-serialized {header, message} pair. The server verifies the signature using the sender's registered public key (from spp.dci.sender.registry) or by fetching the sender's JWKS.

For the exact signing string format, digest computation, and signature-header grammar, see DCI Protocol Details.

Clients may use OAuth 2.0 to authenticate to external registries#

When OpenSPP acts as a DCI client (querying an external registry), the spp.dci.data.source record may be configured to use OAuth 2.0 client-credentials to obtain a bearer token from the external registry's authorization server, then include that token on outbound DCI requests. This is the external registry's choice, not a DCI-spec requirement. See OpenSPP as DCI Client.

Base URL and endpoint prefix#

The DCI server mounts under the FastAPI root_path /dci_api/v1 (configured in spp_dci_server/data/fastapi_endpoint_data.xml). Registry endpoints are then nested under /social/registry/, giving full URLs like:

  • POST https://openspp.example.org/dci_api/v1/social/registry/sync/search

  • GET https://openspp.example.org/dci_api/v1/.well-known/jwks.json

Illustrative request#

# NOTE: requires a signed DCI envelope body; this is a simplified example
curl -X POST https://openspp.example.org/dci_api/v1/social/registry/sync/search \
  -H "Authorization: Bearer <your-allowlisted-token>" \
  -H "Content-Type: application/json" \
  -d @signed_search_request.json

For the complete signed-envelope structure, see DCI Protocol Details.

What's next#