How it works
Generate a key
Go to Settings → API Keys and create a named key for each tool you connect.
Discover & log
Fetch your property IDs from GET /api/v1/properties, then POST entries, list them with GET, and refine with PATCH.
Hours are logged
Entries appear in your Activity Ledger instantly, approved and ready for tax reporting.
Authentication
Every request must include your API key using one of these headers:
Authorization: Bearer reps_YOUR_KEY_HEREor
X-API-Key: reps_YOUR_KEY_HEREStore your API key in an environment variable (e.g. REPS_API_KEY). Never hard-code it in source code or commit it to version control.
Scopes
Scopes restrict what an API key is allowed to do. You choose scopes when creating or rotating a key in Settings → API Keys.
Backward-compatible: Keys created before scopes were introduced have an empty scopes list (scopes: []), which is treated as unrestricted — full access to all endpoints. Those keys keep working unchanged.
| Scope | Grants access to |
|---|---|
time_entries:read | GET /api/v1/time-entries |
time_entries:write | POST /api/v1/time-entries, PATCH /api/v1/time-entries/:id |
properties:read | GET /api/v1/properties |
A key with no scopes selected (empty array) is unrestricted and may call any endpoint. Use specific scopes to follow the principle of least privilege.
Error responses
All API errors return a consistent JSON shape so they are easy to handle programmatically.
// 401 — Missing or invalid API key
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired API key",
"suggestion": "Check that your key starts with 'reps_' and has not been revoked."
}
}
// 403 — Correct key, insufficient scope
{
"error": {
"code": "FORBIDDEN",
"message": "Missing required scope: time_entries:write",
"suggestion": "Rotate your key and add the 'time_entries:write' scope in Settings → API Keys."
}
}
// 400 — Validation failure
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"field": "hours",
"suggestion": "hours must be between 0.001 and 24"
}
}| Field | Always present | Description |
|---|---|---|
error.code | Yes | Machine-readable error identifier (e.g. UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR) |
error.message | Yes | Human-readable description of what went wrong |
error.field | No | The request field that caused a validation failure |
error.suggestion | No | Actionable hint to resolve the error |
Quick start
Log your first hour in under a minute.
curl -X POST https://repsshield.com/api/v1/time-entries \
-H "Authorization: Bearer reps_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-03-06",
"hours": 1.5,
"activityType": "Property Management",
"description": "Reviewed lease renewal for Unit 3B"
}'API Reference
Base URL: https://repsshield.com
/api/v1/time-entriesCreate a new time entry on behalf of the key owner. The primary endpoint for external tools and AI agents logging hours. Optionally attach evidence at creation time via evidence.
Request body (JSON)
| Field | Type | Description |
|---|---|---|
datereq | string | ISO 8601 date — e.g. "2026-03-06" |
hoursreq | number | Decimal hours — min 0.001, max 24. E.g. 1.5 = 1h 30m |
activityTypereq | string | Category of real estate work (see common values below) |
description | string | Free-text description of the specific work done |
propertyId | number | Property ID from GET /api/v1/properties |
propertyType | string | "Long-term Rental" or "Short-term Rental" |
isMaterialParticipation | boolean | Whether this counts toward material participation |
isQualifiedNonMaterial | boolean | Whether this is qualified non-material participation |
timeStarted | string | Exact start timestamp, e.g. "2026-03-06T09:00:00Z" |
timeEnded | string | Exact end timestamp, e.g. "2026-03-06T10:30:00Z" |
evidence | object | object[] | Attach evidence at creation. Single item or array. See evidence schema below. |
Supply one of two shapes (or an array of either):
// Base64-encoded file (recommended for agents)
{ "base64": "<base64 string>", "filename": "receipt.pdf", "mimeType": "application/pdf" }
// Remote HTTPS URL (must be public; HTTP and private IPs are rejected)
{ "url": "https://example.com/receipt.pdf", "filename": "receipt.pdf" }Evidence is uploaded to cloud storage and AI-analysed automatically. Supported types: PDF, images (JPEG/PNG/WebP), plain text, Word docs.
Common activityType values
Property ManagementTenant CommunicationMaintenance & RepairsAdministrativeFinancial ReviewLease ManagementProperty MarketingResearch & AnalysisTravelResponse
{
"id": 4821,
"userId": "user_abc123",
"date": "2026-03-06T00:00:00.000Z",
"hours": "1.50",
"activityType": "Property Management",
"description": "Reviewed lease renewal for Unit 3B",
"source": "manual",
"approvalStatus": "approved",
"createdAt": "2026-03-06T10:32:00.000Z"
}Key management
Session-authenticated endpoints (Settings → API Keys) and the agent-onboarding endpoint accessible via an existing API key.
/api/api-keys/api/api-keys/api/api-keys/:id/rotate/api/api-keys/:id/api/v1/auth/api-keys// Agents can self-provision keys without a browser session.
// Requires an active user session cookie (logged-in user delegates to an agent).
const res = await fetch('https://repsshield.com/api/v1/auth/api-keys', {
method: 'POST',
headers: {
'Authorization': `Bearer ${REPS_API_KEY}`, // existing key with any scope
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'my-agent-v2',
scopes: ['time_entries:read', 'time_entries:write'],
expiresAt: '2027-01-01T00:00:00Z', // optional
}),
});
const { key } = await res.json();
// key.plainKey — store this securely; it is shown only onceThe response body is identical to the regular create-key response: { key: { id, name, scopes, expiresAt, plainKey } }.plainKey is shown only once — store it immediately.
Rotating a key revokes the old key and issues a brand-new one in a single atomic operation. The new key preserves the same name, scopes, and expiry as the original — so your automation only needs to update the secret value.
Security best practices
Use environment variables
Never put your API key in source code or commit it to version control. Use a .env file or your tool's secret store.
One key per tool
Name each key after the tool that uses it so you can revoke access for just that tool without affecting others.
Rotate regularly
Create a new key and revoke the old one from time to time to limit exposure if a key is ever leaked.
Set expiration
When creating a key you can supply an expiresAt date so it self-revokes after a certain period automatically.
Ready to automate your hours?
Generate an API key from your account settings and have your first automated entry logged in minutes.
