Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.propal.io/llms.txt

Use this file to discover all available pages before exploring further.

Error format

All errors follow a consistent JSON structure:
{
  "error": {
    "code": "validation_error",
    "message": "The field 'title' is required.",
    "details": [
      { "field": "title", "message": "Required" }
    ]
  }
}
FieldTypeDescription
codestringMachine-readable error code (see table below).
messagestringHuman-readable error description.
detailsarrayOptional. Field-level validation errors.

HTTP status codes

StatusCodeWhen it happens
200Success (GET, PATCH)
201Resource created (POST)
204Success with no body (DELETE)
400validation_errorInvalid request body or parameters
401unauthorizedMissing or invalid API key
403forbiddenAPI key doesn’t have the required scope
404not_foundResource doesn’t exist or doesn’t belong to your organization
429rate_limit_exceededToo many requests — slow down
500internal_errorSomething went wrong on our end

Error codes reference

Your API key is missing, invalid, or expired.Common causes:
  • Missing Authorization header
  • Typo in the API key
  • Key was revoked
  • Key has expired
Fix: Check your API key and make sure it’s included as Bearer pp_live_... in the Authorization header.
Your API key is valid but doesn’t have permission for this action.Common causes:
  • Calling a write endpoint with a read-only key
  • Missing the required scope for this resource
Fix: Create a new API key with the required scopes, or update the existing key’s permissions.
The requested resource doesn’t exist.Common causes:
  • Invalid UUID in the URL
  • Resource was deleted
  • Resource belongs to a different organization
Fix: Verify the resource ID. Remember that API keys are scoped to one organization — you can’t access resources from other organizations.
The request body or query parameters are invalid.Example response:
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed.",
    "details": [
      { "field": "title", "message": "Required" },
      { "field": "slug", "message": "String must contain at least 1 character(s)" }
    ]
  }
}
Fix: Check the details array for specific field-level errors and correct your request.
You’ve exceeded your rate limit for this time window.Fix: Wait until the Retry-After header value (in seconds) has elapsed, then retry. See Rate Limiting.
An unexpected error occurred on our servers.Fix: Retry the request after a brief delay. If the error persists, contact support at contact@propal.io with the request details.

Handling errors in code

async function createLead(apiKey, leadData) {
  const response = await fetch('https://api.propal.io/v1/leads', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(leadData),
  });

  if (!response.ok) {
    const { error } = await response.json();

    switch (response.status) {
      case 401:
        throw new Error('Invalid API key — check your credentials');
      case 403:
        throw new Error(`Missing permission: ${error.message}`);
      case 429:
        const retryAfter = response.headers.get('Retry-After');
        console.log(`Rate limited. Retry after ${retryAfter}s`);
        break;
      default:
        throw new Error(`API error: ${error.message}`);
    }
  }

  return response.json();
}

Retry strategy

For transient errors (429, 500), we recommend exponential backoff:
  1. Wait 1 second, retry
  2. Wait 2 seconds, retry
  3. Wait 4 seconds, retry
  4. Wait 8 seconds, retry
  5. Give up after 4 retries
For 429 responses, always respect the Retry-After header instead of using your own delay.