Quick Start
Get started with the BildOut API in just a few steps:
- Register your application - Contact us to get your OAuth credentials
- Implement OAuth flow - Direct users to authorize your app
- Make API calls - Use access tokens to fetch data
- Handle webhooks - Subscribe to real-time events
Authentication
The BildOut API uses OAuth 2.0 with the Authorization Code flow. This ensures users explicitly grant access to their data.
Scopes
| Scope | Description |
|---|---|
| invoices:read | Read invoice data |
| invoices:write | Create and update invoices |
| payments:read | Read payment data |
| customers:read | Read customer data |
| customers:write | Create and update customers |
| projects:read | Read project data |
| projects:write | Create and update projects |
| vendors:read | Read vendor data |
| vendors:write | Create and update vendors |
| webhooks:manage | Manage webhook subscriptions |
OAuth Flow
// Using fetch to exchange authorization code
const response = await fetch('https://bildout.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'https://yourapp.com/callback',
client_id: process.env.BILDOUT_CLIENT_ID,
client_secret: process.env.BILDOUT_CLIENT_SECRET,
}),
});
const { access_token, refresh_token, expires_in } = await response.json();Rate Limits
API requests are rate limited per access token:
- Standard: 100 requests per minute
- Burst: 20 requests per second
Rate limit information is included in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1703980800Invoices
List Invoices
GET /api/v1/invoices
const response = await fetch(
'https://bildout.com/api/v1/invoices?limit=20&status=sent',
{
headers: {
'Authorization': `Bearer ${accessToken}`,
},
}
);
const { data: invoices, pagination } = await response.json();
console.log(`Found ${invoices.length} invoices`);Create Invoice
POST /api/v1/invoices
const invoice = await fetch('https://bildout.com/api/v1/invoices', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: customerId,
due_date: '2025-02-15',
line_items: [
{
name: 'Construction Materials',
quantity: 100,
unit_price: 45.00,
tax_rate: 8.5,
},
{
name: 'Labor - Installation',
quantity: 16,
unit_price: 75.00,
},
],
notes: 'Thank you for your business!',
}),
}).then(r => r.json());
console.log(`Created invoice: ${invoice.data.invoice_number}`);Payments
Payments represent money received from customers (incoming) or paid to vendors (outgoing).
Endpoints
GET /api/v1/payments- List paymentsGET /api/v1/payments/:id- Get payment details
Customers
Customers are the people or businesses you invoice for your work.
Endpoints
GET /api/v1/customers- List customersPOST /api/v1/customers- Create customerGET /api/v1/customers/:id- Get customerPUT /api/v1/customers/:id- Update customerDELETE /api/v1/customers/:id- Delete customer
Projects
Projects represent construction jobs. Link invoices and payments to projects for detailed financial tracking.
Endpoints
GET /api/v1/projects- List projectsPOST /api/v1/projects- Create projectGET /api/v1/projects/:id- Get projectPUT /api/v1/projects/:id- Update projectDELETE /api/v1/projects/:id- Delete projectGET /api/v1/projects/:id/financials- Get financial summary
Vendors
Vendors are suppliers, subcontractors, and consultants you pay for materials and services.
Endpoints
GET /api/v1/vendors- List vendorsPOST /api/v1/vendors- Create vendorGET /api/v1/vendors/:id- Get vendorPUT /api/v1/vendors/:id- Update vendorDELETE /api/v1/vendors/:id- Delete vendor
Webhooks
Webhooks notify your application in real-time when events occur in BildOut. Subscribe to specific events to keep your systems in sync.
Event Types
Invoices
invoice.createdinvoice.updatedinvoice.sentinvoice.paidinvoice.deleted
Payments
payment.receivedpayment.sent
Customers
customer.createdcustomer.updatedcustomer.deleted
Projects
project.createdproject.updatedproject.deleted
Verifying Signatures
All webhook payloads are signed with HMAC-SHA256. Always verify signatures before processing webhooks.
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signatureHeader: string,
secret: string
): boolean {
// Parse header: t=timestamp,v1=signature
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t='))?.slice(2);
const signature = parts.find(p => p.startsWith('v1='))?.slice(3);
if (!timestamp || !signature) return false;
// Check timestamp is recent (within 5 minutes)
const age = Math.abs(Date.now() / 1000 - parseInt(timestamp));
if (age > 300) return false;
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}Error Handling
The API uses standard HTTP status codes and returns JSON error responses:
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": [
{ "field": "email", "message": "Invalid email format" }
],
"request_id": "req_abc123xyz"
}
}Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | invalid_request | Malformed request |
| 401 | unauthorized | Invalid or expired token |
| 403 | insufficient_scope | Token lacks required scope |
| 404 | not_found | Resource not found |
| 422 | validation_error | Invalid field values |
| 429 | rate_limit_exceeded | Too many requests |
| 500 | server_error | Internal server error |