Skip to content

Getting Started with Bill Tracking API

Table of Contents

Prerequisites

Before you begin integrating with the Bill Tracking API, ensure you have:

Required Tools

  • HTTP Client: Postman, curl, or any programming language HTTP library
  • JSON Parser: Built-in or library support for JSON parsing
  • API Testing Tool: Postman (recommended) or similar

Required Information

  • Tenant Code: Unique identifier for your organization (provided during onboarding)
  • API Version: Currently 1.0
  • Base URL: https://engagifii-billtracking.azurewebsites.net

Development Environment Setup

bash
# Test API connectivity
curl -I https://engagifii-billtracking.azurewebsites.net

# Expected response: HTTP/2 200 or similar success code

Authentication Setup

The Bill Tracking API uses a header-based authentication system with tenant isolation.

Step 1: Obtain Your Credentials

Contact the Engagifii support team to receive:

  • Your unique tenant-code
  • API access permissions
  • Rate limit information

Step 2: Configure Request Headers

All API requests must include these mandatory headers:

http
api-version: 1.0
tenant-code: YOUR_TENANT_CODE
Content-Type: application/json

Step 3: Validate Your Setup

Test your authentication with a simple endpoint:

bash
curl -X GET "https://engagifii-billtracking.azurewebsites.net/api/1.0/dropdown/states" \
  -H "api-version: 1.0" \
  -H "tenant-code: YOUR_TENANT_CODE" \
  -H "Content-Type: application/json"

Making Your First API Call

Let's walk through a complete API integration from authentication to data retrieval.

Example 1: Get List of States

javascript
// JavaScript (Node.js with axios)
const axios = require('axios');

const API_BASE_URL = 'https://engagifii-billtracking.azurewebsites.net';
const TENANT_CODE = 'YOUR_TENANT_CODE';
const API_VERSION = '1.0';

async function getStates() {
  try {
    const response = await axios.get(
      `${API_BASE_URL}/api/${API_VERSION}/dropdown/states`,
      {
        headers: {
          'api-version': API_VERSION,
          'tenant-code': TENANT_CODE,
          'Content-Type': 'application/json'
        }
      }
    );
    
    console.log('States:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error fetching states:', error.response?.data || error.message);
  }
}

getStates();

Example 2: Get Bill Calendar Events

python
# Python with requests library
import requests
import json

API_BASE_URL = 'https://engagifii-billtracking.azurewebsites.net'
TENANT_CODE = 'YOUR_TENANT_CODE'
API_VERSION = '1.0'

def get_calendar_events(filter_data):
    url = f"{API_BASE_URL}/api/{API_VERSION}/bill/event/calendar"
    
    headers = {
        'api-version': API_VERSION,
        'tenant-code': TENANT_CODE,
        'Content-Type': 'application/json'
    }
    
    response = requests.post(url, headers=headers, json=filter_data)
    
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code} - {response.text}")
        return None

# Example filter
calendar_filter = {
    "startDate": "2025-01-01",
    "endDate": "2025-01-31",
    "stateIds": [1, 2, 3]  # Replace with actual state IDs
}

events = get_calendar_events(calendar_filter)
if events:
    print(f"Found {len(events)} calendar events")

Example 3: Create Activity Log Entry

csharp
// C# with HttpClient
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class BillTrackingApiClient
{
    private readonly HttpClient _httpClient;
    private const string API_BASE_URL = "https://engagifii-billtracking.azurewebsites.net";
    private const string TENANT_CODE = "YOUR_TENANT_CODE";
    private const string API_VERSION = "1.0";
    
    public BillTrackingApiClient()
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("api-version", API_VERSION);
        _httpClient.DefaultRequestHeaders.Add("tenant-code", TENANT_CODE);
    }
    
    public async Task<bool> SaveActivityLog(object logModel)
    {
        var url = $"{API_BASE_URL}/api/{API_VERSION}/activity/log/save";
        var json = JsonConvert.SerializeObject(logModel);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await _httpClient.PostAsync(url, content);
        
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<bool>(result);
        }
        
        throw new Exception($"API call failed: {response.StatusCode}");
    }
}

Testing Your Integration

Using Postman

  1. Import our Postman Collection

  2. Configure Environment Variables

    json
    {
      "tenant_code": "YOUR_TENANT_CODE",
      "api_version": "1.0",
      "base_url": "https://engagifii-billtracking.azurewebsites.net"
    }
  3. Run Collection Tests

    • Execute the included test suite
    • Verify all endpoints return expected responses

Using cURL

bash
# Test suite script
#!/bin/bash

BASE_URL="https://engagifii-billtracking.azurewebsites.net"
TENANT_CODE="YOUR_TENANT_CODE"
API_VERSION="1.0"

# Test 1: Get States
echo "Testing: Get States"
curl -X GET "$BASE_URL/api/$API_VERSION/dropdown/states" \
  -H "api-version: $API_VERSION" \
  -H "tenant-code: $TENANT_CODE" \
  -H "Content-Type: application/json"

# Test 2: Get Sessions
echo -e "\n\nTesting: Get Sessions"
curl -X GET "$BASE_URL/api/$API_VERSION/dropdown/sessions" \
  -H "api-version: $API_VERSION" \
  -H "tenant-code: $TENANT_CODE" \
  -H "Content-Type: application/json"

# Test 3: Get Bill Types
echo -e "\n\nTesting: Get Bill Types"
curl -X GET "$BASE_URL/api/$API_VERSION/dropdown/billtypes" \
  -H "api-version: $API_VERSION" \
  -H "tenant-code: $TENANT_CODE" \
  -H "Content-Type: application/json"

Common Integration Patterns

Pattern 1: Paginated Data Retrieval

Many endpoints support pagination. Here's how to handle paginated responses:

javascript
async function getAllDataWithPagination(endpoint, pageSize = 50) {
  let allData = [];
  let pageNumber = 1;
  let hasMore = true;
  
  while (hasMore) {
    const response = await axios.post(
      `${API_BASE_URL}/api/${API_VERSION}/${endpoint}`,
      {
        pageNumber: pageNumber,
        pageSize: pageSize
      },
      {
        headers: {
          'api-version': API_VERSION,
          'tenant-code': TENANT_CODE,
          'Content-Type': 'application/json'
        }
      }
    );
    
    allData = allData.concat(response.data.collection);
    
    // Check if there are more pages
    hasMore = response.data.pagingModel && 
              pageNumber < response.data.pagingModel.totalPages;
    pageNumber++;
  }
  
  return allData;
}

Pattern 2: Filtering and Searching

Most list endpoints support filtering:

python
def search_bills(search_criteria):
    """
    Search bills with multiple filter criteria
    """
    filter_model = {
        "text": search_criteria.get("keyword", ""),
        "stateIds": search_criteria.get("states", []),
        "sessionIds": search_criteria.get("sessions", []),
        "committeeIds": search_criteria.get("committees", []),
        "startDate": search_criteria.get("start_date"),
        "endDate": search_criteria.get("end_date"),
        "pageNumber": 1,
        "pageSize": 100,
        "sortBy": "lastModified",
        "isAscending": False
    }
    
    # Remove None values
    filter_model = {k: v for k, v in filter_model.items() if v is not None}
    
    response = requests.post(
        f"{API_BASE_URL}/api/{API_VERSION}/bill/search",
        headers=headers,
        json=filter_model
    )
    
    return response.json()

Pattern 3: Batch Operations

For efficient data synchronization:

javascript
async function batchSync(dataType, items) {
  const BATCH_SIZE = 100;
  const results = [];
  
  for (let i = 0; i < items.length; i += BATCH_SIZE) {
    const batch = items.slice(i, i + BATCH_SIZE);
    
    try {
      const response = await axios.post(
        `${API_BASE_URL}/api/${API_VERSION}/DataSync/${dataType}/batch`,
        batch,
        { headers: defaultHeaders }
      );
      
      results.push({
        batch: i / BATCH_SIZE + 1,
        success: true,
        processed: response.data
      });
    } catch (error) {
      results.push({
        batch: i / BATCH_SIZE + 1,
        success: false,
        error: error.message
      });
    }
  }
  
  return results;
}

Pattern 4: File Upload

For endpoints that accept file uploads:

javascript
const FormData = require('form-data');
const fs = require('fs');

async function uploadFile(filePath, metadata) {
  const form = new FormData();
  form.append('file', fs.createReadStream(filePath));
  form.append('metadata', JSON.stringify(metadata));
  
  const response = await axios.post(
    `${API_BASE_URL}/api/${API_VERSION}/file/upload`,
    form,
    {
      headers: {
        ...form.getHeaders(),
        'api-version': API_VERSION,
        'tenant-code': TENANT_CODE
      }
    }
  );
  
  return response.data;
}

Troubleshooting

Common Issues and Solutions

1. Authentication Errors

Problem: 401 Unauthorized or 403 Forbidden

Solutions:

  • Verify your tenant-code is correct
  • Ensure both required headers are present
  • Check if your tenant has access to the requested endpoint
  • Verify API version compatibility
bash
# Debug headers
curl -v -X GET "https://engagifii-billtracking.azurewebsites.net/api/1.0/dropdown/states" \
  -H "api-version: 1.0" \
  -H "tenant-code: YOUR_TENANT_CODE"

2. Rate Limiting

Problem: 429 Too Many Requests

Solutions:

  • Implement exponential backoff
  • Check rate limit headers in response
  • Cache frequently accessed data
javascript
async function requestWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.response?.status === 429) {
        const retryAfter = error.response.headers['retry-after'] || Math.pow(2, i);
        console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Max retries exceeded');
}

3. Invalid Request Format

Problem: 400 Bad Request

Solutions:

  • Validate JSON syntax
  • Check required fields in request body
  • Verify data types match schema
  • Review API documentation for endpoint-specific requirements
python
# Validate JSON before sending
import json

def validate_and_send(endpoint, data):
    try:
        # Validate JSON
        json_str = json.dumps(data)
        json.loads(json_str)  # Validate it can be parsed
        
        # Send request
        response = requests.post(
            f"{API_BASE_URL}/api/{API_VERSION}/{endpoint}",
            headers=headers,
            json=data
        )
        
        return response
    except json.JSONDecodeError as e:
        print(f"Invalid JSON: {e}")
        return None

4. Timeout Issues

Problem: Request timeout on large datasets

Solutions:

  • Use pagination for large result sets
  • Implement streaming for file downloads
  • Set appropriate timeout values
javascript
// Configure timeout
const axiosInstance = axios.create({
  baseURL: API_BASE_URL,
  timeout: 30000, // 30 seconds
  headers: {
    'api-version': API_VERSION,
    'tenant-code': TENANT_CODE
  }
});

5. CORS Issues (Browser-based Applications)

Problem: Cross-Origin Resource Sharing errors

Solutions:

  • Use a backend proxy for API calls
  • Configure CORS on your server
  • Contact support for CORS whitelist addition
javascript
// Proxy configuration example (Node.js Express)
app.use('/api', createProxyMiddleware({
  target: 'https://engagifii-billtracking.azurewebsites.net',
  changeOrigin: true,
  headers: {
    'api-version': '1.0',
    'tenant-code': process.env.TENANT_CODE
  }
}));

Getting Help

If you continue to experience issues:

  1. Check API Status: https://status.engagifii.com
  2. Review Error Details: Include full error response in support requests
  3. Contact Support: api-support@engagifii.com with:
    • Tenant code
    • Request timestamp
    • Complete request/response details
    • Error messages

Best Practices Summary

  1. Always include required headers in every request
  2. Implement proper error handling with retry logic
  3. Use pagination for large data sets
  4. Cache frequently accessed data to reduce API calls
  5. Log all API interactions for debugging
  6. Validate input data before sending requests
  7. Handle rate limits gracefully with exponential backoff
  8. Keep your integration code modular and reusable

Next Step: Review Authentication for detailed security implementation