Geldstuck uses standard HTTP status codes and returns a consistent JSON error body. You never need to parse free-text messages - every error has a stable code.
Error shape
{
"error" : {
"type" : "validation_error" ,
"code" : "email_invalid" ,
"message" : "The provided email address is not valid." ,
"param" : "email" ,
"requestId" : "req_01HX3ZE..."
}
}
Field Description typeBroad category. One of authentication_error, permission_error, validation_error, not_found, conflict, rate_limit_error, api_error. codeStable, machine-readable identifier. Switch on this in your code. messageHuman-readable summary. Safe to surface in logs; do not show to end users without localization. paramField name that caused the error (when applicable). requestIdEchoed from the x-request-id response header. Include this when filing a support ticket.
HTTP status codes
Code Meaning Retry? 200 / 201Success. - 400Bad request. validation_error in the body. No - fix the request. 401Authentication failed. Missing, invalid, or revoked key. No. 403Authenticated, but the key lacks permission. No. 404Resource not found, or not visible to this tenant. No. 409Conflict. Duplicate resource, or illegal state transition. No. 429Rate limited. Yes, with exponential backoff - honor Retry-After. 500Geldstuck had a problem. Yes, with backoff. 503Temporary outage. Yes, with backoff.
Common error codes
Authentication
Code When key_missingNo x-api-key / x-api-secret header. key_invalidKey pair does not match any active key. key_revokedKey has been revoked. signature_invalid(Webhook handler) Geldstuck-Signature did not match.
Validation
Code When email_invalidEmail failed RFC 5322 validation. field_requiredRequired field missing - param names the field. field_too_longparam exceeded its max length.enum_invalidparam is not one of the allowed values.
Resource
Code When resource_not_foundThe object doesn’t exist, or belongs to a different tenant. duplicate_emailTrying to create a user with an email that already exists for this tenant. kyc_already_completedKYC record has been finalized and can’t be resubmitted. transaction_not_pendingTransaction is no longer in a state that allows the requested action.
Rate limits
Code When rate_limit_exceededYou’ve exceeded the per-key rate limit. Retry after Retry-After seconds.
Handling errors idiomatically
Node.js (fetch)
Python (requests)
const res = await fetch ( ` ${ BASE } /tenants/add-user` , {
method: "POST" ,
headers: authHeaders ,
body: JSON . stringify ({ name: "Ada" , email: "ada@example.com" }),
});
if (! res . ok ) {
const { error } = await res . json ();
if ( error . code === "duplicate_email" ) {
// Merge with the existing user
} else if ( error . type === "rate_limit_error" ) {
const wait = Number ( res . headers . get ( "retry-after" ) ?? 1 );
await new Promise (( r ) => setTimeout ( r , wait * 1000 ));
// retry
} else {
logger . error ({ requestId: error . requestId }, error . message );
throw new Error ( error . message );
}
}
Always log requestId. When you email support, we can pull the exact request, response, and timing from a single ID.