Skip to content

Error Handling

The Partners API uses a standardized error handling approach to provide consistent, secure error responses without exposing internal system details.

Error Response Format

All API errors follow a consistent JSON structure:

json
{
  "message": "Human-readable error message",
  "code": "ERROR_CODE",
  "statusCode": 400
}

Response Fields

FieldTypeDescription
messagestringHuman-readable error description
codestringStandardized error code for programmatic handling
statusCodeintegerHTTP status code

Standard Error Codes

The API uses these standardized error codes across all endpoints:

CodeMessageHTTP StatusDescription
VALIDATION_ERROR"Invalid request parameters"400Invalid input data or failed validation
AUTHENTICATION_ERROR"Authentication required"401Missing or invalid Bearer token
FORBIDDEN"Access denied"403Attempting to access resources not owned by partner
NOT_FOUND"Resource not found"404Merchant, tag, or resource doesn't exist
VALIDATION_ERROR"DNS verification pending..."422DNS records not yet propagated (verification endpoint)
SERVICE_ERROR"Unable to process request"500Database errors or internal failures

Authentication Errors

Authentication failures always return a consistent response to prevent information leakage:

json
{
  "message": "Authentication required",
  "code": "AUTHENTICATION_ERROR",
  "statusCode": 401
}

Common Authentication Issues

  • Missing Authorization header
  • Invalid Bearer token format (must be Bearer mp_live_...)
  • Revoked or expired token
  • Malformed token structure

Validation Errors

The API validates all request parameters using strict DTOs. Validation failures return:

json
{
  "message": "Invalid request parameters",
  "code": "VALIDATION_ERROR",
  "statusCode": 400
}

Common Validation Issues

  • Missing required fields
  • Invalid UUID format for IDs
  • Invalid domain format (e.g., including protocol or www prefix)
  • Out-of-range pagination values (page < 1 or limit > 100)
  • Invalid data types

Resource Access Errors

Resource Not Found

When a requested resource doesn't exist:

json
{
  "message": "Resource not found",
  "code": "NOT_FOUND",
  "statusCode": 404
}

Access Denied

When attempting to access another partner's resources:

json
{
  "message": "Access denied",
  "code": "FORBIDDEN",
  "statusCode": 403
}

Special Error Cases

Some operations have specific error scenarios:

Domain Already Registered

When attempting to register a domain that's already in use:

json
{
  "message": "Domain already registered",
  "code": "VALIDATION_ERROR",
  "statusCode": 409
}

DNS Verification Pending

When DNS records haven't propagated yet during verification:

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "DNS verification pending. Please allow time for DNS propagation and try again",
    "statusCode": 422,
    "timestamp": "2026-02-16T08:15:31.482Z",
    "path": "/v1/partners/marktag/verify",
    "method": "POST"
  }
}

DNS Verification Failed

When DNS record exists but points to an incorrect domain:

json
{
  "message": "Tag verification failed. Please check your DNS configuration",
  "code": "VALIDATION_ERROR",
  "statusCode": 400
}

Internal Server Errors

All database errors, external service failures, and unexpected errors return a generic response to prevent information leakage:

json
{
  "message": "Unable to process request",
  "code": "SERVICE_ERROR",
  "statusCode": 500
}

This includes:

  • Database connection errors
  • External service timeouts
  • Unexpected application errors
  • Configuration issues

Error Handling Best Practices

1. Use Error Codes for Logic

Always check the code field rather than parsing error messages:

javascript
// Good ✅
if (error.code === "AUTHENTICATION_ERROR") {
  await refreshToken();
}

// Bad ❌
if (error.message.includes("Authentication")) {
  await refreshToken();
}

2. Implement Retry Logic

For transient failures, implement exponential backoff:

javascript
async function apiCallWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);

      if (response.ok) return response;

      const error = await response.json();

      // Only retry on server errors
      if (error.code === "SERVICE_ERROR" && i < maxRetries - 1) {
        await new Promise((resolve) =>
          setTimeout(resolve, Math.pow(2, i) * 1000),
        );
        continue;
      }

      throw error;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

3. Handle Authentication Errors

Implement token refresh logic for 401 errors:

javascript
async function makeAuthenticatedRequest(url, token) {
  const response = await fetch(url, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

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

    if (error.code === "AUTHENTICATION_ERROR") {
      // Refresh token and retry
      const newToken = await refreshAuthToken();
      return makeAuthenticatedRequest(url, newToken);
    }

    throw error;
  }

  return response;
}

4. Validate Input Before Sending

Check request parameters to avoid validation errors:

javascript
function validateDomain(domain) {
  // Remove protocol if present
  domain = domain.replace(/^https?:\/\//, "");

  // Remove www prefix
  domain = domain.replace(/^www\./, "");

  // Validate domain format
  const domainRegex = /^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
  if (!domainRegex.test(domain)) {
    throw new Error("Invalid domain format");
  }

  return domain;
}

Error Handling Examples

Complete Error Handling Implementation

javascript
class PartnersAPIClient {
  constructor(token) {
    this.token = token;
    this.baseURL = "https://api-alpha.markopolo.ai/v1/partners";
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;

    try {
      const response = await fetch(url, {
        ...options,
        headers: {
          Authorization: `Bearer ${this.token}`,
          "Content-Type": "application/json",
          ...options.headers,
        },
      });

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

      return await response.json();
    } catch (error) {
      // Network or parsing error
      console.error("Request failed:", error);
      throw {
        code: "NETWORK_ERROR",
        message: "Network request failed",
      };
    }
  }

  handleError(error) {
    switch (error.code) {
      case "AUTHENTICATION_ERROR":
        // Trigger re-authentication flow
        this.onAuthenticationError?.();
        break;

      case "VALIDATION_ERROR":
        // Log validation errors for debugging
        console.error("Validation failed:", error.message);
        break;

      case "NOT_FOUND":
        // Handle missing resources
        console.warn("Resource not found");
        break;

      case "FORBIDDEN":
        // Handle access denied
        console.error("Access denied to resource");
        break;

      case "SERVICE_ERROR":
        // Retry with backoff for server errors
        console.error("Server error, consider retrying");
        break;

      default:
        // Check for 422 status (DNS pending)
        if (error.statusCode === 422) {
          console.log("DNS propagation pending, retry in a few minutes");
        }
        break;
    }

    throw error;
  }

  async createMerchant(name) {
    try {
      return await this.request("/merchant", {
        method: "POST",
        body: JSON.stringify({ name }),
      });
    } catch (error) {
      if (error.code === "VALIDATION_ERROR") {
        console.error("Invalid merchant name provided");
      }
      throw error;
    }
  }

  async generateMarkTag(merchantId, domain) {
    // Validate domain before sending
    domain = this.validateDomain(domain);

    try {
      return await this.request("/marktag/generate", {
        method: "POST",
        body: JSON.stringify({ merchantId, domain }),
      });
    } catch (error) {
      if (
        error.code === "VALIDATION_ERROR" &&
        error.message.includes("already registered")
      ) {
        console.error("Domain is already registered");
      }
      throw error;
    }
  }

  validateDomain(domain) {
    domain = domain.replace(/^https?:\/\//, "").replace(/^www\./, "");

    if (!domain.match(/^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/)) {
      throw new Error("Invalid domain format");
    }

    return domain;
  }
}

// Usage
const client = new PartnersAPIClient("mp_live_YOUR_TOKEN");

client.onAuthenticationError = () => {
  // Handle re-authentication
  window.location.href = "/login";
};

// Make API calls with automatic error handling
try {
  const merchant = await client.createMerchant("Acme Corp");
  const marktag = await client.generateMarkTag(
    merchant.merchantId,
    "example.com",
  );
} catch (error) {
  // Errors are already logged by the client
  // Handle based on error code
  if (error.code === "SERVICE_ERROR") {
    showUserMessage("Temporary issue, please try again");
  }
}

Common Error Scenarios

Creating a Merchant

ScenarioError CodeResolution
Missing merchant nameVALIDATION_ERRORProvide a valid merchant name
Database issueSERVICE_ERRORRetry the request

Generating MarkTag

ScenarioError CodeResolution
Invalid domain formatVALIDATION_ERRORUse format: example.com (no protocol/www)
Merchant doesn't existNOT_FOUNDCreate merchant first
Domain already registeredVALIDATION_ERRORDomain is already in use by another partner
Partner domain mismatchVALIDATION_ERRORUse your assigned partner domain for preverified tags

Verifying MarkTag

ScenarioError CodeHTTP StatusResolution
Tag doesn't existNOT_FOUND404Check tag ID is correct
DNS not propagatedVALIDATION_ERROR422Wait for DNS propagation (5-30 minutes typical)
DNS misconfiguredVALIDATION_ERROR400Check CNAME record configuration
Not your tagFORBIDDEN403Can only verify tags you created
Server errorSERVICE_ERROR500Retry with exponential backoff

Retrieving Events

ScenarioError CodeResolution
Invalid date rangeVALIDATION_ERRORUse valid ISO 8601 date format
Page out of rangeVALIDATION_ERRORUse page number within valid range
Merchant not foundNOT_FOUNDCheck merchant ID exists

Support

If you encounter persistent errors or need assistance:

  • Check your API token is valid and not expired
  • Verify your request format matches the documentation
  • Ensure DNS records are properly configured for domain verification
  • Contact partners@markopolo.ai for technical support