OpenSPP Documentation v2.0

⌘ K
  • Products
    • OpenSPP SP-MIS
    • OpenSPP Social Registry
    • OpenSPP Farmer Registry
    • OpenSPP DRIMS
    • Features
      • Unified registry
      • GIS & land management
      • Program management
      • Eligibility & targeting
      • Payment & disbursement
      • In-Kind benefits
      • Data integration & APIs
      • Change management
      • Grievance redress
    • What's new in OpenSPP v2
  • Learn
    • Registry
    • Programs
    • Cycles
    • Eligibility
    • Compliance
    • Entitlements
    • Payments
    • Deduplication
    • Change requests
  • Get started
    • Installing OpenSPP
      • Docker installation
    • Module installation
      • SP-MIS installation
      • Social Registry installation
      • Farmer Registry installation
    • Explore OpenSPP
    • Your first household
      • Step 1: Access the Registry
      • Step 2: Create a Household
      • Step 3: Add members
    • Your first program
      • Step 1: Access the program section
      • Step 2: Create your first social protection program
      • Step 3: Import and enroll beneficiaries
      • Step 4: Understanding program cycles
      • Step 5: Distribute entitlements
    • From Proof of Concept to Pilot
  • User guide
    • Getting started
      • Navigating the OpenSPP interface
      • Administrating role-based access
    • Registry
      • Register an individual
      • Register a group
      • Search and filter registrants
      • Import registrant data
      • Export registrant data
    • Change Requests
      • Submit a change request
      • Review a change request
      • Change request types
    • Case management
      • Core case management workflow
    • Programs
      • Create programs
      • Manage in-kind products
      • Work with program cycles
      • Enroll beneficiaries
      • Allocate funds to programs
      • Manage entitlements
    • Payments
      • View Service Points
    • Approvals
      • Review and Approve Requests
    • Grievance Redress Mechanism (GRM)
      • Manage grievances
    • Geographic Information System (GIS)
      • View maps and generate reports
    • Reference
      • Geographic Areas
      • Vocabularies (Code Lists)
  • Configuration guide
    • OpenSPP Studio
      • Studio Overview
      • Custom Fields (Registry Field Builder)
      • Event Type Designer
      • Change Request Builder
    • CEL expressions
      • CEL quick start
      • CEL syntax reference
      • Variables
      • CEL cookbook
      • CEL troubleshooting
    • Eligibility rules
      • CEL expressions for eligibility
      • Geographic targeting
      • Expression templates
      • Testing eligibility rules
      • Advanced eligibility configuration
    • Entitlement formulas
      • Cash calculations
      • In-kind and basket entitlements
      • Formula library
      • Dynamic entitlements
      • Conditional logic
    • Vocabulary System
      • Vocabulary Overview
      • Standard Vocabularies
      • Vocabulary Profiles
      • Custom Vocabularies
    • Variables & Indicators
      • Variables Overview
      • Creating Variables
      • Variable Types
      • Using Variables in CEL
    • Event Data
      • Event Data Overview
      • Configuring Event Types
      • Defining Event Fields
    • Change request types
      • Overview
      • Creating change request types
      • Field mappings
      • Conflict and duplicate detection
      • Common configuration patterns
      • Custom detail models
      • Troubleshooting
    • Consent configuration
      • Consent management overview
      • Configuring privacy notices
      • Recording consent
      • API consent filtering
    • Role configuration
      • Access control overview
      • Assigning roles to users
      • Predefined roles
      • Creating custom roles
      • Troubleshooting
  • Developer guide
    • Development Setup
    • Architecture
      • CEL Internals
    • Extending OpenSPP
      • Customize Program
      • Customize Cycle
    • API V2
      • API V2 Overview
      • Authentication
      • External Identifiers
      • Consent Management
      • API Resources
      • Search and Filtering
      • Batch Operations
      • Error Handling
      • Studio API Integration
    • DCI Integration
      • DCI Overview
      • OpenSPP as DCI Server
      • OpenSPP as DCI Client
      • DCI Protocol Details
    • Verifiable Credentials
      • Verifiable Credentials Overview
      • W3C Verifiable Credentials Data Model
      • OpenID for Verifiable Credential Issuance
      • Verifiable Credentials Implementation Guide
    • Other Integrations
  • Operations guide
    • Deployment
      • Production Hardening
    • Security
      • Access Control
      • Data Classification
      • PII Encryption
      • Key Management
      • Audit Logging
      • Security Scanning
    • Storage
    • Backup & Recovery
    • Monitoring & Alerts
  • Reference
    • Modules Reference
      • Alerts
      • Analytics
      • API V2
        • Change Request
        • Cycles
        • Data
        • Entitlements
        • GIS API
        • Products
        • Service Points
        • Simulation API
        • Vocabulary
      • Approval Workflows
      • Area Management
      • HDX COD Integration
      • Attachment Antivirus Scan
      • Audit
      • Banking / Bank Details
      • Base (Common)
      • Base Settings
      • Branding Kit
      • Case Management Base
        • CEL Rules
        • Demo Data
        • Entitlements Integration
        • Graduation Integration
        • Programs Integration
        • Registry Integration
        • Session Integration
      • CEL Domain Query Builder
        • Event Data Integration
        • Registry Search
        • Vocabulary Integration
        • Expression Widget
      • Change Request V2
        • Advanced Types
        • Base Types
      • QR Credentials (Claim 169)
      • Consent
      • Custom Fields
      • DCI Core
        • Client
        • Client - CRVS
        • Client - Disability
        • Client - IBR
        • Demo
        • Server
      • Demo
      • Disability Registry
      • Document Management System
      • DRIMS - Disaster Response Inventory Management
        • Sri Lanka Configuration
        • Sri Lanka Demo
      • Encryption: Base
      • Event Data
      • Farmer Registry
        • Change Request Types
        • Dashboard
        • Demo
        • Vocabularies
      • GIS
        • Indicators
        • Reports
        • Reports - Programs
        • Registrant GIS
      • Graduation Management
      • Grievance Redress Mechanism
        • Case Link
        • CEL Rules
        • Demo Data
        • Programs Integration
        • Registry Integration
      • Hazard & Emergency Management
        • Programs Integration
      • Hide Menus Base
      • HXL Integration
        • Area Integration
      • Import Match
      • Indicator
      • Indicator Studio
      • Irrigation
      • Key Management
      • Land Record
      • Metric
      • Metric Service
      • MIS Demo V2
      • API: Oauth
      • Programs
      • Registrant GIS
      • Registry
      • Registry Group Hierarchy
      • Registry Search Portal
      • Scoring
        • Programs Bridge
      • Security
      • Service Points
      • Session Tracking
      • Targeting Simulation
      • Source Tracking
      • Starter: Farmer Registry
      • Starter: Social Registry
      • Starter: SP-MIS
      • Storage Backend
      • Studio
        • API v2 Integration
        • Change Requests
        • Events
      • User Roles
      • Versioning
      • Vocabulary
      • Theme
    • Vocabulary Reference
    • Humanitarian Terms Glossary
    • OpenSPP Glossary
  • Community and support
    • Contributing
    • Internationalization and Localization
    • Modules Maturity Levels and Development Status Policy
    • Module Lifecycle - Maintainer Role Policy
    • Contributor Covenant Code of Conduct
    • OpenSPP Vulnerability Disclosure Policy
    • Licensing

openspp.org openspp.org

Consent Management – Developer guide
  • repository
  • open issue
  • suggest edit
  • .md
Contents
  • Why Consent Matters
  • How Consent Works
  • Consent Response Headers
  • Response Filtering
    • Full Consent
    • Basic Consent (Limited Fields)
    • No Consent
    • Consent Expired
  • Consent Scopes
    • Extension Fields
  • Legal Basis (No Consent Required)
  • Checking Consent Programmatically
    • Example: Python
    • Example: JavaScript
  • Consent API Endpoints
    • Available Operations
    • Get Consent Status
    • Revoke Consent
  • Consent Collection Methods
  • Purpose Limitation
  • Handling Consent Errors
    • 403 Forbidden: Insufficient Consent
    • Consent Scope Mismatch
  • Best Practices
    • 1. Always Check Consent Headers
    • 2. Request Only What You Need
    • 3. Handle Partial Data Gracefully
    • 4. Handle Expired Consent
    • 5. Document Your Purpose
  • Complete Example
  • Are You Stuck?
  • Next Steps
  • See Also

Consent Management

Contents

  • Why Consent Matters
  • How Consent Works
  • Consent Response Headers
  • Response Filtering
    • Full Consent
    • Basic Consent (Limited Fields)
    • No Consent
    • Consent Expired
  • Consent Scopes
    • Extension Fields
  • Legal Basis (No Consent Required)
  • Checking Consent Programmatically
    • Example: Python
    • Example: JavaScript
  • Consent API Endpoints
    • Available Operations
    • Get Consent Status
    • Revoke Consent
  • Consent Collection Methods
  • Purpose Limitation
  • Handling Consent Errors
    • 403 Forbidden: Insufficient Consent
    • Consent Scope Mismatch
  • Best Practices
    • 1. Always Check Consent Headers
    • 2. Request Only What You Need
    • 3. Handle Partial Data Gracefully
    • 4. Handle Expired Consent
    • 5. Document Your Purpose
  • Complete Example
  • Are You Stuck?
  • Next Steps
  • See Also

Consent Management#

This guide is for developers implementing consent-based data access in OpenSPP API V2 integrations.

Why Consent Matters#

OpenSPP API V2 implements privacy-by-design with consent-based access control. This ensures:

  • Data sovereignty: Registrants control who accesses their data

  • Compliance: GDPR, national data protection laws

  • Trust: Transparent data sharing builds community trust

  • Field-level control: Share only what's needed for each purpose

How Consent Works#

┌────────────┐     ┌──────────────┐     ┌───────────────┐
│  API Call  │────►│ Check Consent│────►│ Filter Fields │
│            │     │              │     │   Based on    │
│            │     │ - Registrant │     │   Consent     │
│            │     │ - API Client │     │   Scope       │
│            │     │ - Resource   │     │               │
└────────────┘     └──────────────┘     └───────────────┘
                           │
                           │ No Consent?
                           ▼
                   ┌──────────────┐
                   │Return Minimal│
                   │  (ID only)   │
                   └──────────────┘

When you request a registrant's data:

  1. API checks for active consent from the registrant to your organization

  2. If consent exists, API checks the consent scope (which fields allowed)

  3. API filters the response to only include consented fields

  4. If no consent exists, API returns minimal data (identifiers only)

Consent Response Headers#

API responses include consent status headers:

X-Consent-Status: active
X-Consent-Scope: individual

Header

Description

X-Consent-Status

active, no_consent, expired, legal_basis, scope_mismatch

X-Consent-Scope

Resource type the consent applies to (e.g., individual, group)

Response Filtering#

Full Consent#

When consent is active and covers the requested data:

GET /api/v2/spp/Individual/urn:gov:ph:psa:national-id|PH-123456789
Authorization: Bearer TOKEN

Response:

HTTP/1.1 200 OK
X-Consent-Status: active
X-Consent-Scope: individual

{
  "resourceType": "Individual",
  "identifier": [...],
  "name": {"given": "Maria", "family": "Santos"},
  "birthDate": "1985-03-15",
  "gender": {...},
  "telecom": [...],
  "address": [...]
}

Basic Consent (Limited Fields)#

When consent allows only basic information:

HTTP/1.1 200 OK
X-Consent-Status: active
X-Consent-Scope: individual

{
  "resourceType": "Individual",
  "identifier": [...],
  "name": {"given": "Maria", "family": "Santos"},
  "active": true
}

Note: Only identifier, name, and active fields returned.

No Consent#

When no consent exists:

HTTP/1.1 200 OK
X-Consent-Status: no_consent

{
  "resourceType": "Individual",
  "identifier": [
    {
      "system": "urn:gov:ph:psa:national-id",
      "value": "PH-123456789"
    }
  ],
  "_consent": {
    "status": "no_consent",
    "message": "No active consent for this data access"
  }
}

Note: Only identifiers returned. This allows you to know the record exists without exposing personal data.

Consent Expired#

HTTP/1.1 200 OK
X-Consent-Status: expired

{
  "resourceType": "Individual",
  "identifier": [...],
  "_consent": {
    "status": "expired",
    "message": "Consent has expired"
  }
}

Consent Scopes#

Consent is granted at different levels:

Scope

Fields Included

individual:basic

identifier, name, active

individual:demographic

basic + birthDate, gender

individual:contact

demographic + telecom, address

individual:full

All fields

individual:custom

Administrator-defined field list

Extension Fields#

Module extensions require explicit consent:

GET /api/v2/spp/Individual/...?_extensions=farmer

If consent doesn't include the farmer extension:

{
  "resourceType": "Individual",
  "identifier": [...],
  "name": {...},
  "_consent": {
    "status": "active",
    "message": "Extension 'farmer' not included in consent scope",
    "allowedExtensions": []
  }
}

Legal Basis (No Consent Required)#

Some API clients operate under legal basis that doesn't require individual consent:

Legal Basis

Example

Consent Required

consent

Mobile app for beneficiaries

Yes

legal_obligation

Tax authority audit

No

vital_interest

Emergency health services

No

public_task

Government statistical office

No

contract

Payment processor

No

API clients with legal basis bypass consent checks but still respect scope restrictions.

Checking Consent Programmatically#

Example: Python#

import requests

def check_consent_status(identifier, token, base_url):
    """Check consent status from API response headers."""
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(
        f"{base_url}/Individual/{identifier}",
        headers=headers
    )

    consent_status = response.headers.get("X-Consent-Status")
    consent_scope = response.headers.get("X-Consent-Scope")

    return {
        "status": consent_status,
        "scope": consent_scope,
        "data": response.json()
    }

# Usage
result = check_consent_status(
    identifier="urn:gov:ph:psa:national-id|PH-123456789",
    token=token,
    base_url="https://api.openspp.org/api/v2/spp"
)

if result["status"] == "active":
    print(f"Consent active, scope: {result['scope']}")
    print(f"Data: {result['data']}")
elif result["status"] == "no_consent":
    print("No consent on file. Request consent from registrant.")
else:
    print(f"Consent status: {result['status']}")

Example: JavaScript#

async function checkConsentStatus(identifier, token, baseUrl) {
  const response = await fetch(`${baseUrl}/Individual/${identifier}`, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  const consentStatus = response.headers.get("X-Consent-Status");
  const consentScope = response.headers.get("X-Consent-Scope");

  const data = await response.json();

  return {
    status: consentStatus,
    scope: consentScope,
    data: data,
  };
}

// Usage
const result = await checkConsentStatus(
  "urn:gov:ph:psa:national-id|PH-123456789",
  token,
  "https://api.openspp.org/api/v2/spp",
);

if (result.status === "active") {
  console.log(`Consent active, scope: ${result.scope}`);
} else if (result.status === "no_consent") {
  console.log("No consent on file");
}

Consent API Endpoints#

{note} Consent records are created and managed through the OpenSPP user interface, not via the API. The API provides read-only access to consent status and supports consent revocation.

Available Operations#

Operation

Endpoint

Description

Get Status

GET /Consent/{id}

Check consent status

Revoke

POST /Consent/{id}/$revoke

Revoke consent (GDPR Art 7.3)

Revoke (DELETE)

DELETE /Consent/{id}

Alternative revocation method

Receipt

GET /Consent/{id}/$receipt

Generate ISO 29184 receipt

History

GET /Consent/{id}/$history

View consent version history

Access Summary

GET /Consent/{id}/$access-summary

View data access logs

Get Consent Status#

GET /api/v2/spp/Consent/{consent-id}
Authorization: Bearer TOKEN

Response:

{
  "consent_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "given",
  "grantee": "Ministry of Agriculture",
  "effective_date": "2024-01-15",
  "expiry_date": "2025-06-30",
  "scopes": [
    {
      "resource_type": "individual",
      "field_access": "full",
      "purpose": "service_delivery",
      "include_extensions": true
    }
  ],
  "legal_basis": null
}

Revoke Consent#

POST /api/v2/spp/Consent/{consent-id}/$revoke
Authorization: Bearer TOKEN
Content-Type: application/json

{
  "reason": "Beneficiary requested revocation via support ticket #12345"
}

Response:

HTTP/1.1 200 OK

{
  "resourceType": "Consent",
  "status": "revoked",
  "revokedDate": "2024-11-28T14:30:00Z",
  "revokedBy": {
    "reference": "User/admin",
    "display": "System Administrator"
  },
  "revocationReason": "Beneficiary requested revocation via support ticket #12345"
}

Consent Collection Methods#

When consent is created through the OpenSPP UI, the collection method is recorded:

Method

Description

written

Paper form with signature

verbal

Verbal consent (witnessed)

electronic

Digital signature, checkbox, etc.

implied

Implied by service request

Purpose Limitation#

Consent is purpose-specific. When created through the UI, administrators specify the purpose:

Purpose

Description

service_delivery

Delivering program benefits

eligibility_verification

Checking program eligibility

analytics

Anonymized statistics

research

Academic research

audit

Compliance audits

Handling Consent Errors#

403 Forbidden: Insufficient Consent#

HTTP/1.1 403 Forbidden

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "forbidden",
      "details": {
        "coding": [
          {
            "system": "urn:openspp:error",
            "code": "CONSENT_REQUIRED"
          }
        ],
        "text": "No active consent for this data access"
      },
      "diagnostics": "Registrant has not consented to data sharing with your organization"
    }
  ]
}

Consent Scope Mismatch#

Requesting fields not covered by consent:

GET /api/v2/spp/Individual/...?_elements=identifier,name,telecom,address

With consent only for basic scope (identifier, name):

{
  "resourceType": "Individual",
  "identifier": [...],
  "name": {...},
  "_consent": {
    "status": "active",
    "scope": "individual:basic",
    "message": "Requested fields 'telecom', 'address' not included in consent scope",
    "allowedFields": ["identifier", "name", "active"]
  }
}

Best Practices#

1. Always Check Consent Headers#

def fetch_with_consent_check(url, token):
    """Fetch data and handle consent status."""
    response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
    response.raise_for_status()

    consent_status = response.headers.get("X-Consent-Status")

    if consent_status == "no_consent":
        print("Warning: No consent. Limited data returned.")
    elif consent_status == "expired":
        print("Warning: Consent expired. Limited data returned.")
    elif consent_status != "active":
        print(f"Warning: Consent status: {consent_status}")

    return response.json()

2. Request Only What You Need#

Don't request all fields if you only need basic info:

# ✅ Good - Request only needed fields
GET /api/v2/spp/Individual/...?_elements=identifier,name

# ❌ Bad - Request everything
GET /api/v2/spp/Individual/...

3. Handle Partial Data Gracefully#

Always check if expected fields are present:

individual = fetch_individual(identifier, token, base_url)

# ✅ Good - Check before accessing
if "telecom" in individual:
    phone = individual["telecom"][0]["value"]
else:
    phone = None  # Not consented

# ❌ Bad - Assume field exists
phone = individual["telecom"][0]["value"]  # May crash

4. Handle Expired Consent#

Check the X-Consent-Status header for expiration:

consent_status = response.headers.get("X-Consent-Status")

if consent_status == "expired":
    print("Warning: Consent has expired. Contact registrant for renewal.")
    # Limited data will be returned

5. Document Your Purpose#

Always specify why you're accessing data:

# In your application documentation
PURPOSE = "eligibility_verification"
PURPOSE_DESCRIPTION = "Checking eligibility for farmer subsidy program"

# Include in audit logs
logger.info(f"Accessing individual {identifier} for purpose: {PURPOSE}")

Complete Example#

import requests

class ConsentAwareClient:
    """API client with consent awareness."""

    def __init__(self, client_id, client_secret, base_url):
        self.base_url = base_url
        self.client_id = client_id
        self.token = None  # Implement token management

    def get_individual_with_consent(self, identifier):
        """Fetch individual with consent checking."""
        headers = {"Authorization": f"Bearer {self.token}"}
        response = requests.get(
            f"{self.base_url}/Individual/{identifier}",
            headers=headers
        )
        response.raise_for_status()

        # Check consent status from headers
        consent_status = response.headers.get("X-Consent-Status")
        consent_scope = response.headers.get("X-Consent-Scope")

        data = response.json()

        # Add consent metadata to response
        data["_consentInfo"] = {
            "status": consent_status,
            "scope": consent_scope,
        }

        # Warn if consent issues
        if consent_status == "no_consent":
            data["_consentInfo"]["warning"] = "No consent on file. Limited data returned."
        elif consent_status == "expired":
            data["_consentInfo"]["warning"] = "Consent has expired. Limited data returned."

        return data

    def revoke_consent(self, consent_id, reason=None):
        """Revoke a consent via API."""
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json"
        }

        payload = {"reason": reason} if reason else {}

        response = requests.post(
            f"{self.base_url}/Consent/{consent_id}/$revoke",
            headers=headers,
            json=payload
        )
        response.raise_for_status()
        return response.json()

# Usage
client = ConsentAwareClient(
    client_id="ministry-of-agriculture",
    client_secret="your-secret",
    base_url="https://api.openspp.org/api/v2/spp"
)

# Fetch with consent checking
individual = client.get_individual_with_consent(
    "urn:gov:ph:psa:national-id|PH-123456789"
)

if individual["_consentInfo"]["status"] == "active":
    print(f"Full data available: {individual['name']['text']}")
elif individual["_consentInfo"]["status"] == "no_consent":
    print("Limited data. Consent must be obtained through OpenSPP UI.")

Are You Stuck?#

Getting only identifiers in responses?

This indicates no consent. Check the X-Consent-Status header. If no_consent, the registrant needs to provide consent through the OpenSPP user interface.

How do I know what fields are allowed?

Check the _consent.allowedFields array in responses with consent restrictions.

Can I bypass consent for emergencies?

If your API client has legal_basis: vital_interest, consent checks are relaxed. Contact your administrator to configure this.

Consent expired—what now?

The registrant must renew consent through the OpenSPP user interface. Contact your program administrator.

Can registrants revoke consent themselves?

Yes, through the beneficiary portal or mobile app. Your integration should handle revoked consent gracefully (you'll receive limited data).

Next Steps#

  • API Resources - Learn about available API resources

  • Search and Filtering - Advanced search capabilities

  • Error Handling - Error handling

  • Authentication - OAuth 2.0 setup

See Also#

  • GDPR Principles - Data protection principles

  • G2P Connect: Consent - Consent in social protection

  • FHIR Consent Resource - FHIR consent model

previous

External Identifiers

next

API Resources

By The OpenSPP community
© Copyright OpenSPP.

The text and illustrations in this website are licensed by the OpenSPP Project under a Creative Commons Attribution 4.0 International license. All other trademarks are owned by their respective owners.