Givebear LogoGivebear
Connect API

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>

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)
end
req, _ := 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.

ScopeGrants
donations:readView donation records, amounts, refunds, and dispute status.
donors:readView donor contact records and their giving history.
payouts:readView payouts deposited to the organization's bank account.
events:readView events (including drafts) with their ticket types, dates, and capacity.
registrations:readView event attendee records: names, emails, ticket, payment and check-in status.
reference:readView campaigns, funds, and the public profile so ids on donations resolve.
payments:writeCreate donation payment intents. Money settles to the org's own Stripe account.
webhooks:writeCreate, 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"
  }
}
StatustypeWhen
400invalid_requestMalformed body, or a bad limit, cursor, or updated_since.
401unauthorizedMissing, invalid, revoked, or expired credential.
403insufficient_scopeThe credential lacks the scope the route requires.
404not_foundThe resource does not exist in this organization.
429rate_limitedToo 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

Was this page helpful?

On this page