Developer API

Log hours from any tool, automatically.

The REPSShield API lets your AI agents, scripts, and automations create time entries on your behalf — so your IRS REPS hours are always up to date.

How it works

1

Generate a key

Go to Settings → API Keys and create a named key for each tool you connect.

2

Discover & log

Fetch your property IDs from GET /api/v1/properties, then POST entries, list them with GET, and refine with PATCH.

3

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_HERE

or

X-API-Key: reps_YOUR_KEY_HERE

Store 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.

ScopeGrants access to
time_entries:readGET /api/v1/time-entries
time_entries:writePOST /api/v1/time-entries, PATCH /api/v1/time-entries/:id
properties:readGET /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"
  }
}
FieldAlways presentDescription
error.codeYesMachine-readable error identifier (e.g. UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR)
error.messageYesHuman-readable description of what went wrong
error.fieldNoThe request field that caused a validation failure
error.suggestionNoActionable 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

POST/api/v1/time-entries
API key required
time_entries:write

Create 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)

FieldTypeDescription
datereqstringISO 8601 date — e.g. "2026-03-06"
hoursreqnumberDecimal hours — min 0.001, max 24. E.g. 1.5 = 1h 30m
activityTypereqstringCategory of real estate work (see common values below)
descriptionstringFree-text description of the specific work done
propertyIdnumberProperty ID from GET /api/v1/properties
propertyTypestring"Long-term Rental" or "Short-term Rental"
isMaterialParticipationbooleanWhether this counts toward material participation
isQualifiedNonMaterialbooleanWhether this is qualified non-material participation
timeStartedstringExact start timestamp, e.g. "2026-03-06T09:00:00Z"
timeEndedstringExact end timestamp, e.g. "2026-03-06T10:30:00Z"
evidenceobject | object[]Attach evidence at creation. Single item or array. See evidence schema below.
Evidence schema

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 & AnalysisTravel

Response

201 Created
Entry created — full time entry object returned
{
  "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"
}
400
Validation error — check the errors array
401
Missing, invalid, or expired API key
403
Key lacks the time_entries:write scope
500
Server error — try again or contact support

Key management

Session-authenticated endpoints (Settings → API Keys) and the agent-onboarding endpoint accessible via an existing API key.

GET/api/api-keys
POST/api/api-keys
POST/api/api-keys/:id/rotate
DELETE/api/api-keys/:id
POST/api/v1/auth/api-keys
Agent self-provisioning example
// 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 once

The 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.