{
  "openapi": "3.1.0",
  "info": {
    "title": "UGP Giving Service",
    "version": "2026-06-25",
    "description": "Canonical REST interface for the UGP Giving service (`dev.ugp.giving`). Schema references are logical pointers - actual payload shape is determined by negotiated capabilities.\n\n**Endpoint Resolution:** This spec defines operations only. The base URL MUST be obtained from the nonprofit's discovery profile at `/.well-known/ucp` under `services[\"dev.ugp.giving\"][transport=rest].endpoint`. The nonprofit hosts the profile on its own domain, but the giving endpoints are hosted by a provider (e.g. Chariot); the `endpoint` therefore points at the provider, not the nonprofit. The `{endpoint}` server variable below is a placeholder for tooling compatibility."
  },
  "servers": [
    {
      "url": "{endpoint}",
      "description": "Provider-hosted endpoint from UGP discovery profile",
      "variables": {
        "endpoint": {
          "default": "https://provider.example.com/api/ugp/v1/orgs/example-org",
          "description": "Obtain from /.well-known/ucp → services[\"dev.ugp.giving\"][transport=rest].endpoint"
        }
      }
    }
  ],
  "paths": {
    "/profile": {
      "get": {
        "operationId": "get_profile",
        "summary": "Get Profile",
        "description": "Retrieve the nonprofit profile configuration served by the giving provider.",
        "parameters": [
          {
            "$ref": "#/components/parameters/authorization"
          },
          {
            "$ref": "#/components/parameters/x_api_key"
          },
          {
            "$ref": "#/components/parameters/signature"
          },
          {
            "$ref": "#/components/parameters/signature_input"
          },
          {
            "$ref": "#/components/parameters/request_id"
          },
          {
            "$ref": "#/components/parameters/user_agent"
          },
          {
            "$ref": "#/components/parameters/ucp_agent"
          },
          {
            "$ref": "#/components/parameters/content_type"
          },
          {
            "$ref": "#/components/parameters/accept"
          },
          {
            "$ref": "#/components/parameters/accept_language"
          },
          {
            "$ref": "#/components/parameters/accept_encoding"
          }
        ],
        "responses": {
          "200": {
            "description": "Nonprofit profile retrieved",
            "headers": {
              "Signature": {
                "$ref": "#/components/headers/signature"
              },
              "Signature-Input": {
                "$ref": "#/components/headers/signature_input"
              },
              "Content-Digest": {
                "$ref": "#/components/headers/content_digest"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/profile"
                }
              }
            }
          }
        }
      }
    },
    "/donation-batches": {
      "post": {
        "operationId": "create_batch",
        "summary": "Create Donation Batch",
        "description": "Create a donation batch and receive bank payment instructions. Idempotent on `external_batch_id`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/authorization"
          },
          {
            "$ref": "#/components/parameters/x_api_key"
          },
          {
            "$ref": "#/components/parameters/signature"
          },
          {
            "$ref": "#/components/parameters/signature_input"
          },
          {
            "$ref": "#/components/parameters/content_digest"
          },
          {
            "$ref": "#/components/parameters/idempotency_key"
          },
          {
            "$ref": "#/components/parameters/request_id"
          },
          {
            "$ref": "#/components/parameters/user_agent"
          },
          {
            "$ref": "#/components/parameters/ucp_agent"
          },
          {
            "$ref": "#/components/parameters/content_type"
          },
          {
            "$ref": "#/components/parameters/accept"
          },
          {
            "$ref": "#/components/parameters/accept_language"
          },
          {
            "$ref": "#/components/parameters/accept_encoding"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/create_batch_request"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Donation batch created",
            "headers": {
              "Signature": {
                "$ref": "#/components/headers/signature"
              },
              "Signature-Input": {
                "$ref": "#/components/headers/signature_input"
              },
              "Content-Digest": {
                "$ref": "#/components/headers/content_digest"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batch_create_response"
                }
              }
            }
          }
        }
      }
    },
    "/donation-batches/{batch_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/batch_id_path"
        }
      ],
      "get": {
        "operationId": "get_batch",
        "summary": "Get Donation Batch",
        "description": "Get the latest state of a donation batch, including payment tracing and reconciliation results.",
        "parameters": [
          {
            "$ref": "#/components/parameters/authorization"
          },
          {
            "$ref": "#/components/parameters/x_api_key"
          },
          {
            "$ref": "#/components/parameters/signature"
          },
          {
            "$ref": "#/components/parameters/signature_input"
          },
          {
            "$ref": "#/components/parameters/request_id"
          },
          {
            "$ref": "#/components/parameters/user_agent"
          },
          {
            "$ref": "#/components/parameters/ucp_agent"
          },
          {
            "$ref": "#/components/parameters/content_type"
          },
          {
            "$ref": "#/components/parameters/accept"
          },
          {
            "$ref": "#/components/parameters/accept_language"
          },
          {
            "$ref": "#/components/parameters/accept_encoding"
          }
        ],
        "responses": {
          "200": {
            "description": "Donation batch state retrieved",
            "headers": {
              "Signature": {
                "$ref": "#/components/headers/signature"
              },
              "Signature-Input": {
                "$ref": "#/components/headers/signature_input"
              },
              "Content-Digest": {
                "$ref": "#/components/headers/content_digest"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batch_state_response"
                }
              }
            }
          }
        }
      }
    },
    "/donation-batches/{batch_id}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/batch_id_path"
        }
      ],
      "post": {
        "operationId": "update_batch_status",
        "summary": "Update Donation Batch Status",
        "description": "Advance a batch's payment state. The platform reports that it has initiated, sent, or canceled the funding payment.",
        "parameters": [
          {
            "$ref": "#/components/parameters/authorization"
          },
          {
            "$ref": "#/components/parameters/x_api_key"
          },
          {
            "$ref": "#/components/parameters/signature"
          },
          {
            "$ref": "#/components/parameters/signature_input"
          },
          {
            "$ref": "#/components/parameters/content_digest"
          },
          {
            "$ref": "#/components/parameters/idempotency_key"
          },
          {
            "$ref": "#/components/parameters/request_id"
          },
          {
            "$ref": "#/components/parameters/user_agent"
          },
          {
            "$ref": "#/components/parameters/ucp_agent"
          },
          {
            "$ref": "#/components/parameters/content_type"
          },
          {
            "$ref": "#/components/parameters/accept"
          },
          {
            "$ref": "#/components/parameters/accept_language"
          },
          {
            "$ref": "#/components/parameters/accept_encoding"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/status_update_request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Donation batch state updated",
            "headers": {
              "Signature": {
                "$ref": "#/components/headers/signature"
              },
              "Signature-Input": {
                "$ref": "#/components/headers/signature_input"
              },
              "Content-Digest": {
                "$ref": "#/components/headers/content_digest"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batch_state_response"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "batch_id_path": {
        "name": "batch_id",
        "in": "path",
        "required": true,
        "description": "The unique identifier of the donation batch.",
        "schema": {
          "type": "string"
        }
      },
      "authorization": {
        "name": "Authorization",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code)."
      },
      "x_api_key": {
        "name": "X-API-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Authenticates the platform with a reusable api key allocated to the platform by the giving provider."
      },
      "signature": {
        "name": "Signature",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "RFC 9421 HTTP Message Signature. Required when using HTTP Message Signatures for authentication. Format: `sig1=:<base64-signature>:`."
      },
      "signature_input": {
        "name": "Signature-Input",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "RFC 9421 Signature-Input header. Required when using HTTP Message Signatures for authentication. Format: `sig1=(\"@method\" \"@path\" ...);created=<timestamp>;keyid=\"<key-id>\"`."
      },
      "content_digest": {
        "name": "Content-Digest",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Body digest per RFC 9530. Required for requests/responses with a body. Format: `sha-256=:<base64-digest>:`."
      },
      "idempotency_key": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Ensures duplicate operations don't happen during retries."
      },
      "request_id": {
        "name": "Request-Id",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "For tracing the requests across network layers and components."
      },
      "user_agent": {
        "name": "User-Agent",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Identifies the user agent string making the call."
      },
      "ucp_agent": {
        "name": "UCP-Agent",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the signer's profile URI using RFC 8941 Dictionary syntax. The URL MUST point to /.well-known/ucp. Format: profile=\"https://example.com/.well-known/ucp\"."
      },
      "content_type": {
        "name": "Content-Type",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Representation Metadata. Tells the receiver what the data in the message body actually is."
      },
      "accept": {
        "name": "Accept",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Content Negotiation. The client tells the server what data formats it is capable of understanding."
      },
      "accept_language": {
        "name": "Accept-Language",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities."
      },
      "accept_encoding": {
        "name": "Accept-Encoding",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Compression. The client tells the server which content-codings it supports, usually for compression."
      }
    },
    "headers": {
      "signature": {
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "RFC 9421 HTTP Message Signature for response. Contains the signature value in the format `sig1=:<base64-signature>:`."
      },
      "signature_input": {
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "RFC 9421 Signature-Input header for response. Describes signed components, timestamp, and key ID."
      },
      "content_digest": {
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "JCS-canonicalized body digest per RFC 8785. Format: `sha-256=:<base64-digest>:`."
      }
    },
    "schemas": {
      "profile": {
        "$ref": "https://ugp.dev/2026-06-25/schemas/giving/profile.json"
      },
      "create_batch_request": {
        "$ref": "https://ugp.dev/2026-06-25/schemas/giving/donations.json#/$defs/create_batch_request"
      },
      "batch_create_response": {
        "oneOf": [
          {
            "$ref": "https://ugp.dev/2026-06-25/schemas/giving/donations.json#/$defs/batch_response"
          },
          {
            "$ref": "https://ugp.dev/2026-06-25/schemas/common/types/error_response.json"
          }
        ]
      },
      "batch_state_response": {
        "oneOf": [
          {
            "$ref": "https://ugp.dev/2026-06-25/schemas/giving/donations.json#/$defs/batch_state_response"
          },
          {
            "$ref": "https://ugp.dev/2026-06-25/schemas/common/types/error_response.json"
          }
        ]
      },
      "status_update_request": {
        "$ref": "https://ugp.dev/2026-06-25/schemas/giving/donations.json#/$defs/status_update_request"
      }
    }
  }
}
