Skip to main content
This page goes one level deeper than Quickstart. Where Quickstart shows the minimum syntax to get a response, this page walks through what’s actually happening on each side of the request — what you’re sending, what you get back, and what to do when something doesn’t work. The example used throughout is GET /api/Donor/list. It’s a read endpoint with paginated results — a representative case that exercises authentication, pagination, and the standard response envelope.

Anatomy of a request

A successful Raise API request has four pieces:
PieceExample
Method and URLGET https://prod-api.raisedonors.com/api/Donor/list
Authentication headerAuthorization: Bearer eyJhbGciOi...
Query parameters (for read endpoints)?Skip=0&Take=25&SortBy=createdDateTime&Descending=true
Body and content type (for write endpoints)Content-Type: application/json + JSON body
For a GET, the body and content type are omitted. For a POST, PUT, or DELETE with a body, all four pieces apply.

A complete request

cURL
curl -G https://prod-api.raisedonors.com/api/Donor/list \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  --data-urlencode "Skip=0" \
  --data-urlencode "Take=25" \
  --data-urlencode "SortBy=createdDateTime" \
  --data-urlencode "Descending=true"
The same request in JavaScript with URLSearchParams (the safest way to build a query string):
JavaScript
const params = new URLSearchParams({
  Skip: '0',
  Take: '25',
  SortBy: 'createdDateTime',
  Descending: 'true',
});

const response = await fetch(
  `https://prod-api.raisedonors.com/api/Donor/list?${params}`,
  {
    headers: {
      Authorization: `Bearer ${process.env.RAISE_API_TOKEN}`,
    },
  }
);
Three things this request gets right:
  • Query parameters use the casing the API expects. Raise’s list endpoints use PascalCase parameter names (Skip, Take, SortBy, Descending) — sending skip or take lowercase will not be recognized.
  • Boolean parameters are strings on the wire. Query string values are always strings — Descending=true is the literal string "true", not a JSON boolean. The API parses it back to a boolean server-side.
  • The token is in the header, not a query parameter. Never put a Bearer token in a URL — it ends up in server logs, browser histories, and proxy caches. The Authorization header is the only correct place.

Anatomy of a response

A successful response has three parts:
PartExample
Status code200 OK
HeadersContent-Type: application/json, plus standard HTTP headers
BodyThe JSON payload
For a paginated read endpoint, the body follows the items/total envelope:
{
  "items": [
    {
      "id": 12345,
      "firstName": "Bruce",
      "lastName": "Wayne",
      "email": "bruce@wayne.example",
      "createdDateTime": "2024-12-15T14:30:00Z"
    }
  ],
  "total": 8421
}
The two fields:
  • items is the array of records for the current page. Its length is at most Take (or the server’s enforced maximum, whichever is smaller).
  • total is the count of records that match the query across all pages — not just the current page. Use it to determine when you’ve reached the end of the result set during pagination.

Inspecting the response in code

JavaScript
const response = await fetch(url, { headers });

if (!response.ok) {
  // Non-2xx status — see Error Handling
  const error = await response.json();
  throw new Error(`Request failed: ${response.status} ${error.title}`);
}

const data = await response.json();
console.log(`Received ${data.items.length} of ${data.total} total donors`);

for (const donor of data.items) {
  // Process each record
}
response.ok is true for any 2xx status. For non-2xx, parse the body as the ProblemDetails error envelope to extract diagnostic information.

Iterating through all results

A single request returns at most Take records. To process every record matching a query, paginate by advancing Skip until you’ve consumed the full result set:
JavaScript
async function listAllDonors() {
  const donors = [];
  let skip = 0;
  const take = 1000;            // The cap for bulk reads
  let total = null;

  do {
    const params = new URLSearchParams({
      Skip: skip.toString(),
      Take: take.toString(),
      SortBy: 'id',              // Stable sort for resumable iteration
      Descending: 'false',
    });

    const response = await fetch(
      `https://prod-api.raisedonors.com/api/Donor/list?${params}`,
      { headers: { Authorization: `Bearer ${process.env.RAISE_API_TOKEN}` } }
    );

    if (!response.ok) {
      throw new Error(`Page ${skip} failed: ${response.status}`);
    }

    const page = await response.json();
    if (total === null) {
      total = page.total;
      console.log(`Starting iteration over ${total} donors`);
    }

    donors.push(...page.items);
    skip += take;
  } while (skip < total);

  return donors;
}
Three patterns this gets right:
  • The loop condition is skip < total, not page.items.length === take. The last page typically returns fewer items than Take — the boundary check needs to know the full total.
  • total is captured from the first response. It can shift between pages if records are inserted concurrently; capturing it once gives stable bounds for the loop.
  • Sort by id for stable iteration. Default sort orders may produce inconsistent results if records change during pagination. See Pagination and Filtering for the full discussion.

A POST example: submitting a donation

The walkthrough so far has used GET. Writes follow the same auth-and-parsing pattern with the addition of a request body and Content-Type header. The most distinctive Raise write is POST /api/Raise/give:
curl -X POST https://prod-api.raisedonors.com/api/Raise/give \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "organizationId": 1234,
    "campaignId": 5678,
    "amount": 50.00,
    "donor": {
      "firstName": "Bruce",
      "lastName": "Wayne",
      "email": "bruce@wayne.example"
    },
    "paymentMethod": {
      "...": "see Process a Donation workflow"
    }
  }'
The complete request shape for POST /api/Raise/give — including the payment method structure, designation breakdown, and required fields — is in Process a Donation. Do not run this against production data without reading that page first.

Recognizing common failures

Four failure modes are far more common than any others. Recognizing them quickly saves time:
StatusSymptomMost likely cause
401 UnauthorizedRequest rejected immediately, no resource-specific errorThe Authorization header is missing, malformed, or the token is invalid/expired/revoked. See Authentication: errors.
400 Bad RequestRequest reached the endpoint but was rejectedInvalid parameter value, malformed JSON body, or schema validation failure. The ProblemDetails response body identifies the specific issue.
404 Not FoundThe endpoint or resource doesn’t existTypo in the path (e.g., /api/Donors/list instead of /api/Donor/list), or the specific record ID was deleted or never existed.
Network error / TLS errorNo HTTP response at allWrong hostname, missing TLS support, or firewall blocking outbound traffic to prod-api.raisedonors.com.
When you see 401, don’t retry blindly. The same request will fail the same way until the credential is corrected. See Error Recovery Patterns for the full classification.

What to do next

You now have a working request, a parsed response, and a pagination loop. From here, the docs branch by what you want to build:

Error Handling

The full ProblemDetails envelope and status code remediation guide.

Pagination and Filtering

Filter syntax, sort fields, and the iteration patterns for large result sets.

The Raise Data Model

A deeper look at how Donors, Gifts, Forms, and Campaigns relate.

Process a Donation

The end-to-end POST /api/Raise/give workflow with the full request shape.
Last modified on May 21, 2026