Skip to content

Core Concepts

The Universal Giving Protocol (UGP) is the giving vertical of the Universal Commerce Protocol (UCP). It reuses UCP's discovery, profile, capability, service, and signing primitives unchanged, and adds a small set of giving-specific schemas under dev.ugp.giving.

Authority domain: ugp.dev. Protocol version: 2026-06-25.

Roles

UGP maps the UCP role model onto charitable giving.

  • Nonprofit — the recipient organization. In UCP terms the nonprofit is a Business: it hosts a discovery profile at /.well-known/ucp on its own domain and declares the giving capabilities it supports. The nonprofit is the beneficiary of the funds.
  • Grantmaker / Platform — the party collecting and remitting gifts on behalf of donors. In UCP terms this is the Platform. Examples include donor-advised funds (daf), private and community foundations (foundation), crowdfunding sites (crowdfunding), workplace-giving providers (workplace_giving), and IRA qualified-charitable-distribution processors (qcd). The platform discovers a nonprofit, batches the donations it has collected, and moves money to the nonprofit over bank rails.

A giving provider (e.g. Chariot) operates the actual REST endpoints on behalf of nonprofits. See the hosting split below.

A registry operator (e.g. ugp.dev) runs a directory that resolves a nonprofit by name, EIN, or website to its /.well-known/ucp. The registry is a distinct role from the nonprofit and the provider: it indexes profiles but is not the source of truth for any of them. See discovery vs. search below.

The hosting split

UGP deliberately mirrors how UCP separates who advertises a capability from who serves it. In UCP, a merchant hosts its /.well-known/ucp profile on its own domain while a commerce platform such as Shopify hosts the REST endpoint the profile points at. UGP works the same way:

  • The nonprofit hosts /.well-known/ucp on its own domain (e.g. https://eastsideshelter.org/.well-known/ucp). This is what makes the nonprofit independently discoverable and lets it own its identity and signing keys.
  • The giving-service endpoints are hosted by a provider. The profile's services["dev.ugp.giving"][].endpoint points at the provider, not the nonprofit — for example https://chariot.app/api/ugp/v1/orgs/eastside-shelter. The provider implements /profile, /donation-batches, batch status, and reconciliation; the nonprofit never has to run a payments backend.

This separation means many nonprofits can share one provider's infrastructure while each keeps a self-owned, self-signed discovery profile.

Discovery vs. search: the registry

Discovery requires you to already know the domain. Search is the step before: resolving a name, EIN, or website to the domain that hosts the /.well-known/ucp. UGP layers this as a separate service, dev.ugp.registry, run by a registry operator such as ugp.dev and offered for free.

A registry is a directory, not a system of record. A search result is a pointer — primarily the well_known_url — plus enough metadata to disambiguate it. The platform takes that pointer and runs ordinary discovery against the authoritative profile: the money path never goes through the registry. The index is a projection of the nonprofit's well-known, kept fresh by re-indexing on publish/edit and a periodic re-crawl, so it can be free, public, and federated without ever being able to misdirect funds.

Money movement: bank rails, not tokenized payment

UCP shopping moves money by tokenizing a buyer's payment instrument through a payment_handler (cards, wallets, network tokens). UGP does not do this.

Giving money moves over bank rails — ACH, RTP, FedNow, or wire — directly from the platform's bank account into a bank account the provider controls on behalf of the nonprofit. The platform has already collected funds from donors; UGP only standardizes the remittance of those already-collected funds to the nonprofit. There is no card to tokenize and no instrument to charge at the UGP layer.

Because of this, payment_handlers is empty ({}) in a UGP profile, and the supported rails and bank destination live in the donations capability response, not in payment_handlers:

  • The dev.ugp.giving.donations capability config advertises supported_rails and an address_scope.
  • When a batch is created, the batch_response returns a payment_address (the destination account number, routing number, account type, supported rails, and a payment_reference). The platform pushes funds to that address over a supported rail.

Keeping rails in the donations capability — rather than in payment_handlers — keeps UGP honest about what is actually happening: a bank-to-bank transfer of funds the platform already holds, not a UCP-style instrument authorization.