Appearance
Getting Started with Bill Tracking API
Table of Contents
- Prerequisites
- Authentication Setup
- Making Your First API Call
- Testing Your Integration
- Common Integration Patterns
- Troubleshooting
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 codeAuthentication 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/jsonStep 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
Import our Postman Collection
- Download postman-collection.json
- Import into Postman: File → Import → Upload Files
Configure Environment Variables
json{ "tenant_code": "YOUR_TENANT_CODE", "api_version": "1.0", "base_url": "https://engagifii-billtracking.azurewebsites.net" }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-codeis 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 None4. 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:
- Check API Status: https://status.engagifii.com
- Review Error Details: Include full error response in support requests
- Contact Support: api-support@engagifii.com with:
- Tenant code
- Request timestamp
- Complete request/response details
- Error messages
Best Practices Summary
- Always include required headers in every request
- Implement proper error handling with retry logic
- Use pagination for large data sets
- Cache frequently accessed data to reduce API calls
- Log all API interactions for debugging
- Validate input data before sending requests
- Handle rate limits gracefully with exponential backoff
- Keep your integration code modular and reusable
Next Step: Review Authentication for detailed security implementation
