Error Codes

Understanding API error responses helps you build robust integrations. This guide covers all error codes, their meanings, and how to handle them effectively.

Error Response Format

All API errors follow a consistent JSON format:

Error Response Structurejson
{
  "code": "ERROR_CODE",
  "message": "Human-readable error message",
  "data": {}
}

Response Fields

  • code (string): Machine-readable error code
  • message (string): Human-readable error description
  • data (object): Additional error context (usually empty)

HTTP Status Codes

QuickSign API uses standard HTTP status codes to indicate success or failure:

Error CodeHTTP StatusDescription
400400

Invalid request body, validation error, or malformed JSON

Show example
{
  "code": "ERROR",
  "message": "Invalid request body or validation error",
  "data": {}
}
401401

Missing or invalid API key

Show example
{
  "code": "UNAUTHENTICATED",
  "message": "API key is missing or invalid",
  "data": {}
}
403403

Document does not belong to your account or insufficient permissions

Show example
{
  "code": "ERROR",
  "message": "Document does not belong to your account",
  "data": {}
}
404404

Document, recipient, or resource not found

Show example
{
  "code": "ERROR",
  "message": "Document not found",
  "data": {}
}
429429

API rate limit exceeded

Show example
{
  "code": "ERROR",
  "message": "API rate limit exceeded",
  "data": {}
}
500500

Unexpected server error

Show example
{
  "code": "ERROR",
  "message": "Internal server error",
  "data": {}
}

Common Errors

401 Unauthorized - Missing API Key

Missing API Key

401 Unauthorized
{
  "code": "UNAUTHENTICATED",
  "message": "API key is missing or invalid",
  "data": {}
}

Cause: Request was sent without the X-API-KEY header.

Solution: Include your API key in every request:

curl -X GET https://api.quicksign.com.au/external/api/v1/documents/abc123/audit \
  -H "X-API-KEY: your-api-key-here"

401 Unauthorized - Invalid API Key

Invalid API Key

401 Unauthorized
{
  "code": "UNAUTHENTICATED",
  "message": "Invalid API key",
  "data": {}
}

Cause: API key is incorrect, expired, or revoked.

Solution:

  • Verify you're using the correct API key
  • Check if the key has been revoked
  • Generate a new API key if necessary

400 Bad Request - Validation Error

Validation Error

400 Bad Request
{
  "code": "ERROR",
  "message": "Invalid request body or validation error",
  "data": {}
}

Cause: Request body contains invalid or missing required fields.

Common Issues:

  • Missing required fields (name, email, role)
  • Invalid email format
  • Invalid role value (must be "SIGNER" or "VIEWER")
  • Invalid field type (must be "SIGNATURE", "TEXT", "DATE", or "INITIALS")
  • Invalid coordinates or page numbers

Solution: Validate your request body against the API schema:

Valid Request Bodyjson
// Correct format
[
  {
    "name": "John Doe",
    "email": "john@example.com",
    "role": "SIGNER"  // Must be "SIGNER" or "VIEWER"
  }
]

404 Not Found - Document Not Found

Document Not Found

404 Not Found
{
  "code": "ERROR",
  "message": "Document not found",
  "data": {}
}

Cause: Document ID doesn't exist or is invalid.

Solution:

  • Verify the document ID is correct (UUID format)
  • Check that the document hasn't been deleted
  • Ensure you're using the right environment (dev/staging/production)

403 Forbidden - Access Denied

Access Denied

403 Forbidden
{
  "code": "ERROR",
  "message": "Document does not belong to your account",
  "data": {}
}

Cause: You're trying to access a document that doesn't belong to your account.

Solution:

  • Verify you're using the correct API key
  • Check that the document ID belongs to your account
  • Ensure you have the necessary permissions

400 Bad Request - Recipient Not Found

Recipient Not Found

400 Bad Request
{
  "code": "ERROR",
  "message": "Recipient not found for email: john@example.com",
  "data": {}
}

Cause: Trying to add fields for a recipient that hasn't been added to the document.

Solution: Add recipients before adding fields:

Correct Ordertext
// 1. First, add recipients
POST /documents/:id/recipients

// 2. Then, add fields referencing recipient emails
POST /documents/:id/fields

400 Bad Request - Document Not Ready

Document Not Ready

400 Bad Request
{
  "code": "ERROR",
  "message": "Document must have at least one recipient with fields",
  "data": {}
}

Cause: Trying to send a document that doesn't meet the requirements.

Requirements before sending:

  • Document has at least one recipient
  • All recipients have at least one field assigned
  • Document is in "DRAFT" status
  • Expiry date is in the future

429 Too Many Requests - Rate Limit

Rate Limit Exceeded

429 Too Many Requests
{
  "code": "ERROR",
  "message": "API rate limit exceeded",
  "data": {}
}

Cause: You've exceeded your API usage limit.

Solution:

  • Wait before making more requests
  • Implement exponential backoff retry logic
  • Batch multiple operations into single requests
  • Contact support to increase your rate limit
Retry Logicjavascript
// Exponential backoff example
async function apiCallWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}

500 Internal Server Error

Internal Server Error

500 Internal Server Error
{
  "code": "ERROR",
  "message": "Internal server error",
  "data": {}
}

Cause: Unexpected error on QuickSign servers.

Solution:

  • Retry the request after a short delay
  • If the error persists, contact support
  • Check QuickSign status page for known issues

Error Handling Best Practices

1. Always Check Status Codes

Check Status Codesjavascript
const response = await fetch(url, options);

if (!response.ok) {
  const error = await response.json();
  throw new Error(`API Error: ${error.message}`);
}

const data = await response.json();

2. Implement Retry Logic

Implement exponential backoff for transient errors (429, 500, 503):

Robust Retry Logicjavascript
async function apiRequest(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      
      // Success
      if (response.ok) {
        return await response.json();
      }
      
      // Don't retry client errors (400, 401, 403, 404)
      if (response.status >= 400 && response.status < 500) {
        throw new Error(await response.text());
      }
      
      // Retry server errors (500, 503) and rate limits (429)
      if (attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      throw new Error(`Request failed after ${maxRetries} attempts`);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
    }
  }
}

3. Log All Errors

Error Loggingjavascript
try {
  const response = await apiRequest(url, options);
} catch (error) {
  // Log error details
  console.error('API Error:', {
    url,
    method: options.method,
    status: error.status,
    message: error.message,
    timestamp: new Date().toISOString()
  });
  
  // Re-throw or handle accordingly
  throw error;
}

4. Provide User-Friendly Messages

User-Friendly Messagesjavascript
function getErrorMessage(error) {
  const messages = {
    401: 'Authentication failed. Please check your API key.',
    403: 'Access denied. You don't have permission to access this resource.',
    404: 'Document not found. Please verify the document ID.',
    429: 'Too many requests. Please try again later.',
    500: 'Server error. Please try again or contact support.'
  };
  
  return messages[error.status] || 'An unexpected error occurred.';
}

5. Validate Before Sending

Validate data on your end before making API requests:

Client-Side Validationjavascript
function validateRecipient(recipient) {
  if (!recipient.name) {
    throw new Error('Recipient name is required');
  }
  
  if (!recipient.email || !isValidEmail(recipient.email)) {
    throw new Error('Valid email address is required');
  }
  
  if (!['SIGNER', 'VIEWER'].includes(recipient.role)) {
    throw new Error('Role must be SIGNER or VIEWER');
  }
  
  return true;
}

Error Handling Checklist

  • ✅ Check HTTP status codes
  • ✅ Parse error messages
  • ✅ Implement retry logic for transient errors
  • ✅ Log all errors for debugging
  • ✅ Validate data before sending requests
  • ✅ Provide user-friendly error messages
  • ✅ Monitor error rates in production

Testing Error Scenarios

Test your error handling by intentionally triggering errors:

  • 401 Unauthorized: Use an invalid API key
  • 404 Not Found: Use a non-existent document ID
  • 400 Bad Request: Send invalid data (missing required fields)
  • 429 Rate Limit: Make rapid successive requests

Need Help?

Support

If you encounter errors you can't resolve:

Next Steps