Permissions, rate limits and request changes
Date: 2026-10-20
An integration whose API key holds the scopes for what it does is not affected by the permission changes below.
Permissions
Section titled “Permissions”- Dashboard widgets need their permission.
GET /dashboard/kpi/{key},/dashboard/list/{key}and/dashboard/chart/{key}answer403 Forbiddenunless the caller holds the permission the widget needs — for exampleusers:viewforlist.recent-mfa-attempts,orders:refundforlist.recent-refunds,orders:viewforkpi.ytd-giving. The four chart endpoints under/dashboard/charts/needorders:view(donation trend, by fund, payment methods) orcontacts:view(donor growth). An unknown key is still400. GET /dashboard/summaryand/dashboard/homesay which figures they left out. A figure the caller has no permission for is sent as zero, and new fields say which:donorsVisible,donationsVisibleandrecurringRevenueVisibleon the summary;donationTrendVisible,donationsByFundVisible,paymentMethodsVisibleanddonorGrowthVisibleon the home payload. Read the flag before treating a zero as a zero.GET /audit-logs/{id}needsaudit-logs:view, as the list does.GET /users/{userId}/permission-overridesneedsusers:manage-permissions.POST /uploads/imagesneeds a permission that puts a picture somewhere: writing a letter, packing slip, tax statement or contact email template, setting up an event, or managing the organization’s settings. It accepts PNG, JPEG, GIF and WebP, checked by the file’s contents.POST /onboarding/completeneedsimport:manage.POST /api-keystakes only scopes the caller holds, and answersApiKey.ScopeNotHeldnaming any others.POST /orders: ataxAmountOverrideneedsorders:override-tax(Order.TaxOverrideForbidden), and ashippingAmountOverrideother than the chosen shipping option’s own rate needsorders:override-shipping(Order.ShippingOverrideForbidden), as the handling fee does.PUT /tasks/{id}: changing who a task is assigned to needstasks:assign(DonorTask.ReassignForbidden), unless the caller is taking the task themselves.
Requests and responses
Section titled “Requests and responses”POST /fulfillment/packing-slips/generatereturns one PDF (application/pdf) rather than an HTML page.POST /api/auth/change-passwordchanges the signed-in person’s own password; the body takes nouserId. It answers200 OKwith{ "sessionStamp": "…" }, and a new password ends the person’s other sessions.POST /api/auth/reset-passwordanswersAuth.InvalidRequestfor a link that doesn’t work, where it answeredInvalidToken.POST /api/auth/session-statusis new: whether a signed-in session may go on. The web app asks it; an integration has no need to.POST /api/auth/loginandPOST /api/auth/mfa/verifyignore anipAddressin the body; the address is the connection’s.- Every CSV and Excel export writes a value that starts with
=,+,-,@, a tab or a carriage return with a leading apostrophe (in Excel, the quote-prefix style), so a spreadsheet shows it as text. A plain number or phone number keeps its sign. The customer data export is the records as they were typed. POST /imports/uploadrefuses a workbook that unpacks to more than 250 MB or holds more than 10,000 parts (Import.FileTooLarge).- Anonymous endpoints read a bounded body. Sign-in, two-factor, the contact form, signing and
the phone system’s endpoints accept at most 64 KB; the public event pages 256 KB; payment
processor webhooks 1 MB. A larger body is refused with
413 Payload Too Large. - API responses carry security headers:
Content-Security-Policy: default-src 'none',X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy, andStrict-Transport-Securityover HTTPS.
Rate limits
Section titled “Rate limits”- Each API key may make 120 requests a minute. A request past that is answered
429 Too Many Requests; each key has its own minute. A request whose key doesn’t authenticate is counted against its address. - Signing in is limited. Ten wrong passwords for one address within 15 minutes block it for 15
minutes, and longer if it happens again within a day:
400withAuth.TooManyAttempts, even for the right password. Five wrong current passwords within 15 minutes block a password change the same way.POST /api/auth/login,/forgot-passwordand/reset-passwordare also limited per address (429).
See Rate limits.
Webhooks
Section titled “Webhooks”- A webhook address must be a public HTTPS host name.
POSTandPUT /webhooksrefuse an IP address as the host,localhost, a single-label name, a name under.local,.localhost,.internal,.lan,.home.arpa,.intranetor.corp, and a user name or password in the address. A delivery or test send to a name that resolves to a private, loopback or link-local address isn’t sent; its error reads “The address isn’t on the public internet, so nothing was sent.” - Redirects aren’t followed — a
3xxanswer is a failed attempt. Each delivery attempt is one request, on the same retry schedule. POST /webhooks/{id}/testsigns the test as deliveries are signed (sha256={hex}), and itserrorMessageis a sentence.