Skip to main content

Overview

The Offergrid Reseller API lets you browse services, place orders, and track fulfillment programmatically, enabling seamless integration with your existing platforms.

Getting Started

1. Get Your API Key

  1. Sign in to offergrid.io
  2. Go to SettingsAPI Keys
  3. Click Generate New Key
  4. Save the key securely
See Authentication for details.

2. Choose Your Integration Approach

Option A: Real-Time API Calls
  • Search catalog on-demand
  • Place orders immediately
  • Best for interactive applications
Option B: Webhook Notifications
  • Receive order status updates automatically
  • Event-driven architecture
  • Best for automation
Option C: Scheduled Sync
  • Cache catalog data locally
  • Refresh periodically
  • Best for high-volume or offline-capable apps

Common Integration Patterns

Pattern 1: Property Management Software

Integrate Offergrid into property management platforms:
// Example: Add "Order Services" button to tenant portal
async function orderServicesForTenant(tenant, property) {
  // 1. Get available services for property address
  const catalog = await fetchCatalog(property.zipCode);

  // 2. Let tenant select services
  const selectedServices = await showServiceSelection(catalog);

  // 3. Place orders
  const orders = await Promise.all(
    selectedServices.map((service) =>
      placeOrder({
        offerId: service.id,
        customerInfo: {
          fullName: tenant.name,
          email: tenant.email,
          phone: tenant.phone,
        },
        serviceAddress: property.address,
      })
    )
  );

  // 4. Track and notify tenant
  await trackOrders(orders);
}

Pattern 2: Real Estate Platform

Add service ordering to real estate workflows:
// Example: Pre-close service coordination
async function coordinateServicesForClosing(property, buyer) {
  const moveInDate = property.closingDate;

  // Get services available at property
  const services = await getAvailableServices(property.zipCode);

  // Recommend essential services
  const recommended = recommendServices(services, property.type);

  // Place orders timed for move-in
  const orders = await placeOrdersWithTiming(recommended, buyer, moveInDate);

  return orders;
}

Pattern 3: Lead Generation Website

Capture and convert service leads:
// Example: Service comparison tool
async function buildServiceComparison(zipCode, serviceType) {
  // Fetch available offers
  const offers = await fetch(
    `https://api.offergrid.io/reseller/catalog?zipCode=${zipCode}&category=${serviceType}`,
    {
      headers: {
        'x-api-key': process.env.OFFERGRID_API_KEY,
      },
    }
  );

  const services = await offers.json();

  // Build comparison table
  const comparison = services.map((s) => ({
    name: s.name,
    price: s.monthlyPrice,
    features: s.keyFeatures,
    provider: s.providerName,
  }));

  return comparison;
}

// When user selects service
async function captureLeadAndOrder(service, customerInfo) {
  // Save lead to CRM
  await saveToCRM(customerInfo);

  // Place order on Offergrid
  const order = await placeOrder({
    offerId: service.id,
    customerInfo,
    serviceAddress: customerInfo.address,
  });

  // Track order in CRM
  await updateCRMWithOrder(customerInfo.id, order.orderId);

  return order;
}

Core API Operations

Browsing Catalog

# Get all services
GET /reseller/catalog

# Filter by category
GET /reseller/catalog?category=internet

# Filter by location
GET /reseller/catalog?zipCode=94102

# Search
GET /reseller/catalog?search=fiber&minPrice=50&maxPrice=100

Viewing Offer Details

GET /reseller/catalog/{offerId}
Returns complete offer information.

Placing Orders

POST /reseller/orders
Content-Type: application/json

{
  "items": [{ "offerId": "off-123-abc" }],
  "customerInfo": {
    "fullName": "John Doe",
    "email": "john@example.com",
    "phone": "+1-555-123-4567"
  },
  "serviceAddress": {
    "street": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "zipCode": "94102",
    "country": "US"
  }
}

Tracking Orders

# List all orders
GET /reseller/orders

# Get specific order
GET /reseller/orders/{orderId}

# Filter by status
GET /reseller/orders?status=scheduled

Canceling Orders

PATCH /reseller/orders/{orderId}/cancel
Content-Type: application/json

{
  "reason": "Customer no longer needs service"
}

Error Handling

async function safeApiCall(apiFunction, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await apiFunction();
    } catch (error) {
      if (error.status === 401) {
        throw new Error('Invalid API key');
      }

      if (error.status === 429) {
        // Rate limited - wait and retry
        await sleep(2 ** i * 1000);
        continue;
      }

      if (error.status >= 500) {
        // Server error - retry
        await sleep(2 ** i * 1000);
        continue;
      }

      // Client error - don't retry
      throw error;
    }
  }

  throw new Error('Max retries exceeded');
}

Caching Strategies

Catalog Caching

Cache catalog data to reduce API calls:
const catalogCache = {
  data: null,
  timestamp: null,
  ttl: 3600000, // 1 hour

  async get(zipCode) {
    if (this.isValid()) {
      return this.data;
    }

    this.data = await fetchCatalog(zipCode);
    this.timestamp = Date.now();
    return this.data;
  },

  isValid() {
    return this.data && Date.now() - this.timestamp < this.ttl;
  },
};

Order Status Caching

Cache order status with shorter TTL:
const orderCache = new Map();
const ORDER_CACHE_TTL = 300000; // 5 minutes

async function getOrderStatus(orderId) {
  const cached = orderCache.get(orderId);

  if (cached && Date.now() - cached.timestamp < ORDER_CACHE_TTL) {
    return cached.data;
  }

  const order = await fetchOrder(orderId);
  orderCache.set(orderId, {
    data: order,
    timestamp: Date.now(),
  });

  return order;
}

Webhooks for Real-Time Updates

Instead of polling, use webhooks:
app.post('/webhooks/offergrid', async (req, res) => {
  const { event, orderId, status } = req.body;

  if (event === 'order.status_changed') {
    // Update your database
    await updateOrderInDatabase(orderId, status);

    // Notify customer
    await sendCustomerNotification(orderId, status);
  }

  res.sendStatus(200);
});
See Webhooks for setup details.

Rate Limiting

  • Burst: 100 requests per minute
  • Sustained: 10,000 requests per hour
Implement rate limiting:
const RateLimiter = require('bottleneck');

const limiter = new RateLimiter({
  maxConcurrent: 5,
  minTime: 600, // 600ms between requests = 100/min
});

const rateLimitedFetch = limiter.wrap(fetch);

// Use rate-limited fetch
const response = await rateLimitedFetch(url, options);

Best Practices

Catalog changes infrequently. Cache for 1-6 hours to reduce API calls.
Don’t poll for order status. Use webhooks for real-time notifications.
Handle temporary failures with exponential backoff. Retry 429 and 5xx errors.
Check customer info and addresses locally before making API calls.
Map Offergrid order IDs to your internal order/lead IDs for tracking.
Provide clear error messages to users. Don’t expose technical details.

Testing

Use Test Mode

Create test orders without real fulfillment:
{
  "items": [{ "offerId": "test-offer-id" }],
  "customerInfo": {
    "fullName": "Test Customer",
    "email": "test@example.com",
    "phone": "+1-555-000-0000"
  },
  "metadata": {
    "test": true
  }
}

Monitor Integration Health

Track:
  • API success rate
  • Average response time
  • Error rates by endpoint
  • Order acceptance rate

Next Steps