Authentication
Authenticate Connect requests with an org API key or the Connect with Givebear OAuth flow.
Every Connect request carries a bearer token scoped to exactly one organization.
Pick the credential that matches your integration, then send it in the
Authorization header.
Authorization: Bearer <token>API key
For one org's own integration: a script, a backend, or a Zapier hook.
OAuth (Connect with Givebear)
For a platform that serves many organizations.
Authenticate with an API key
Use an API key when you build for a single organization, including your own.
Keys start with gb_live_ and are hashed at rest.
Mint a key
An org owner or admin opens Dashboard -> Developers -> API keys, picks scopes, and creates the key. Copy it once: the full secret is shown one time only.
Send it as a bearer token
Pass the key in the Authorization header on every request.
curl https://givebear.io/api/v1/donations \
-H "Authorization: Bearer gb_live_..."import { GivebearConnect } from "@givebear/connect";
const gb = new GivebearConnect({ token: "gb_live_..." });
const { data } = await gb.donations.list({ limit: 50 });import requests
response = requests.get(
"https://givebear.io/api/v1/donations",
headers={"Authorization": "Bearer gb_live_..."},
)<?php
$ch = curl_init("https://givebear.io/api/v1/donations");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer gb_live_..."]);
$response = curl_exec($ch);
curl_close($ch);require "net/http"
uri = URI("https://givebear.io/api/v1/donations")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer gb_live_..."
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
endreq, _ := http.NewRequest("GET", "https://givebear.io/api/v1/donations", nil)
req.Header.Set("Authorization", "Bearer gb_live_...")
resp, err := http.DefaultClient.Do(req)Revoke a key anytime from the same page. A revoked key returns 401
immediately.
Authenticate with OAuth
Use OAuth when your platform acts on behalf of many organizations. Each org grants access on a consent screen, and you receive a token scoped to that one org. This is the "Connect with Givebear" flow.
Register an app
Register your app under Dashboard -> OAuth apps in your personal
dashboard (apps belong to your account, not an organization) to get a
client_id and a client_secret (the secret starts with gbs_). Set your
redirect URI here.
Send the admin to the authorize endpoint
Redirect the org admin to the authorize endpoint with a space-delimited scope
and a random state. Add offline_access to the scopes to receive a refresh
token.
GET https://givebear.io/api/auth/oauth2/authorize
?response_type=code
&client_id=<client_id>
&redirect_uri=<your_callback>
&scope=donations:read%20reference:read%20offline_access
&state=<random>The admin signs in, picks which organization to connect, and approves. Only org
owners and admins can approve. Givebear redirects back to your callback with
code and state.
Exchange the code for tokens
POST the code to the token endpoint to get an access token and (if you
requested offline_access) a refresh token.
curl https://givebear.io/api/auth/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=<code> \
-d client_id=<client_id> \
-d client_secret=<client_secret> \
-d redirect_uri=<your_callback>const response = await fetch("https://givebear.io/api/auth/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
}),
});
const tokens = await response.json();import requests
tokens = requests.post(
"https://givebear.io/api/auth/oauth2/token",
data={
"grant_type": "authorization_code",
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
},
).json()<?php
$ch = curl_init("https://givebear.io/api/auth/oauth2/token");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
"grant_type" => "authorization_code",
"code" => $code,
"client_id" => $clientId,
"client_secret" => $clientSecret,
"redirect_uri" => $redirectUri,
]));
$tokens = json_decode(curl_exec($ch), true);
curl_close($ch);require "net/http"
require "json"
uri = URI("https://givebear.io/api/auth/oauth2/token")
response = Net::HTTP.post_form(uri, {
"grant_type" => "authorization_code",
"code" => code,
"client_id" => CLIENT_ID,
"client_secret" => CLIENT_SECRET,
"redirect_uri" => REDIRECT_URI,
})
tokens = JSON.parse(response.body)form := url.Values{
"grant_type": {"authorization_code"},
"code": {code},
"client_id": {clientID},
"client_secret": {clientSecret},
"redirect_uri": {redirectURI},
}
resp, err := http.PostForm("https://givebear.io/api/auth/oauth2/token", form)The access token is opaque, starts with gba_, and expires after one hour. The
refresh token starts with gbr_ and lasts 30 days.
Call the API with the access token
Send the access token as a bearer token, exactly like an API key. It is scoped to the single org that approved consent.
curl https://givebear.io/api/v1/organization \
-H "Authorization: Bearer gba_..."import { GivebearConnect } from "@givebear/connect";
const gb = new GivebearConnect({ token: "gba_..." });
const org = await gb.organization.get();org = requests.get(
"https://givebear.io/api/v1/organization",
headers={"Authorization": "Bearer gba_..."},
).json()$ch = curl_init("https://givebear.io/api/v1/organization");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer gba_..."]);
$org = json_decode(curl_exec($ch), true);
curl_close($ch);uri = URI("https://givebear.io/api/v1/organization")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer gba_..."
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
org = JSON.parse(response.body)req, _ := http.NewRequest("GET", "https://givebear.io/api/v1/organization", nil)
req.Header.Set("Authorization", "Bearer gba_...")
resp, err := http.DefaultClient.Do(req)Refresh the access token
When the access token expires, exchange the refresh token for a new one. The call has the same shape as the code exchange above in every language; only the form fields change.
curl https://givebear.io/api/auth/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d refresh_token=<gbr_token> \
-d client_id=<client_id> \
-d client_secret=<client_secret>Organizations see and revoke connected apps under Dashboard -> Developers -> Connected apps. Revoking invalidates that app's tokens for that org immediately.
Scopes
A credential can act only within the scopes it was granted. Request the minimum your integration needs. The consent screen shows the org exactly what it grants.
| Scope | Grants |
|---|---|
donations:read | View donation records, amounts, refunds, and dispute status. |
donors:read | View donor contact records and their giving history. |
payouts:read | View payouts deposited to the organization's bank account. |
events:read | View events (including drafts) with their ticket types, dates, and capacity. |
registrations:read | View event attendee records: names, emails, ticket, payment and check-in status. |
reference:read | View campaigns, funds, and the public profile so ids on donations resolve. |
payments:write | Create donation payment intents. Money settles to the org's own Stripe account. |
webhooks:write | Create, update, and remove the credential's own webhook endpoints. |
A credential can never exceed what the granting member could do. An admin can only grant a scope they hold the matching org permission for.
For the payments:write flow, see the Connect reference.
For webhooks:write, see Webhooks.
Rate limits
Each credential is limited to 120 requests per 60 seconds. Exceeding the limit
returns a 429 with the rate_limited error type. Back off and retry.
Errors
Every non-2xx response uses the same JSON envelope.
{
"error": {
"type": "insufficient_scope",
"message": "This credential is missing the `donations:read` scope"
}
}| Status | type | When |
|---|---|---|
400 | invalid_request | Malformed body, or a bad limit, cursor, or updated_since. |
401 | unauthorized | Missing, invalid, revoked, or expired credential. |
403 | insufficient_scope | The credential lacks the scope the route requires. |
404 | not_found | The resource does not exist in this organization. |
429 | rate_limited | Too many requests from this credential. |
In any language, check the status code and parse the envelope; the
quickstart's handle errors
section shows this in curl, Python, PHP, Ruby, and Go. The @givebear/connect
client throws a ConnectApiError on any non-2xx response. Read its status,
type, and message.
import { ConnectApiError, GivebearConnect } from "@givebear/connect";
const gb = new GivebearConnect({ token: "gb_live_..." });
try {
await gb.donations.list();
} catch (err) {
if (err instanceof ConnectApiError) {
console.error(err.status, err.type, err.message);
}
}Next steps
Quickstart
Make your first authenticated request end to end.
API reference
Every endpoint, parameter, and response shape.
Webhooks
Receive donation and payout events as they happen.
Versioning
How the API changes over time.