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/ucpon 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/ucpon 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"][].endpointpoints at the provider, not the nonprofit — for examplehttps://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.donationscapability config advertisessupported_railsand anaddress_scope. - When a batch is created, the
batch_responsereturns apayment_address(the destination account number, routing number, account type, supported rails, and apayment_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.