Skip to content

Public API

Sina Gulsen edited this page May 29, 2026 · 5 revisions

API Reference

Effort Tracker provides a REST API for programmatic access to effort data.

Base URL

https://efforttrackerapi.sinaware.com/api

Authentication

All API requests require authentication using Azure DevOps access tokens.

Required Headers

Header Description
Authorization Bearer token from Azure DevOps
X-Organization Azure DevOps organization name
X-Project Project name (for project-scoped endpoints)

Getting an Access Token

Create a Personal Access Token (PAT) in Azure DevOps:

  1. Go to your Azure DevOps organization (e.g., https://dev.azure.com/{your-org})
  2. Click the User Settings icon (top right) → Personal access tokens
  3. Click + New Token
  4. Configure the token:
    • Name: e.g., "Effort Tracker API"
    • Organization: Select your organization (or "All accessible organizations")
    • Expiration: Choose an appropriate expiration date
    • Scopes: Select Read under Work Items at minimum
  5. Click Create and copy the token immediately (it won't be shown again)

Use the token in the Authorization header:

curl -H "Authorization: Bearer YOUR_PAT_TOKEN" \
     -H "X-Organization: your-org-name" \
     https://efforttrackerapi.sinaware.com/api/v1/stats/summary

Effort Logs

Get Effort Logs

Retrieve effort logs for a work item.

GET /effortlogs?workItemId={id}&page={page}&pageSize={pageSize}

Query Parameters

Parameter Type Required Description
workItemId integer Yes Work item ID
page integer No Page number (default: 1)
pageSize integer No Items per page (default: 10)

Response

{
  "data": [
    {
      "id": "log-uuid",
      "workItemId": 123,
      "date": "2025-01-15",
      "hours": 4.5,
      "activityType": "Development",
      "notes": "Implemented feature",
      "userId": "user-guid",
      "userDisplayName": "John Doe",
      "projectName": "MyProject",
      "areaPath": "MyProject\\Web",
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ],
  "page": 1,
  "pageSize": 10,
  "totalCount": 25,
  "totalPages": 3
}

Create Effort Log

Create a new effort log entry.

POST /effortlogs
Content-Type: application/json

Request Body

{
  "workItemId": 123,
  "date": "2025-01-15",
  "hours": 4.5,
  "activityType": "Development",
  "notes": "Implemented feature"
}

Response: 201 Created

{
  "id": "log-uuid",
  "workItemId": 123,
  "date": "2025-01-15",
  "hours": 4.5,
  "activityType": "Development",
  "notes": "Implemented feature",
  "userId": "user-guid",
  "userDisplayName": "John Doe"
}

Update Effort Log

Update an existing effort log entry.

PUT /effortlogs/{logId}
Content-Type: application/json

Request Body

{
  "date": "2025-01-16",
  "hours": 5.0,
  "activityType": "Testing",
  "notes": "Updated notes"
}

Response: 200 OK

Delete Effort Log

Delete an effort log entry.

DELETE /effortlogs/{logId}

Response: 204 No Content


Query Effort Logs

Query effort logs with filters.

POST /effortlogs/query
Content-Type: application/json

Request Body

{
  "startDate": "2025-01-01",
  "endDate": "2025-01-31",
  "userIds": ["user-guid-1", "user-guid-2"],
  "activityTypes": ["Development", "Testing"],
  "workItemIds": [123, 456],
  "areaPath": "MyProject\\Web",
  "page": 1,
  "pageSize": 50
}

Response

{
  "data": [...],
  "page": 1,
  "pageSize": 50,
  "totalCount": 150,
  "totalHours": 342.5
}

Export

Export to CSV

Export effort logs as CSV.

POST /effortlogs/export
Content-Type: application/json

Request Body: Same as query

Response: CSV file download


Project Settings

Get Project Settings

Get the current project settings.

GET /api/project-settings

Headers: Authorization, X-Organization, X-Project

Response:

{
  "projectName": "MyProject",
  "restrictDataToOwnUser": false
}

Update Project Settings

Update project settings. Requires Project Admin role.

PUT /api/project-settings

Headers: Authorization, X-Organization, X-Project

Request Body:

{
  "restrictDataToOwnUser": true
}

Response: 200 OK with updated settings

Note: When restrictDataToOwnUser is enabled, non-admin users will only see their own effort logs in queries, exports, and the Work Item tab.


Activity Types

Get Activity Types

Get active activity types for a project.

GET /activitytypes

Response

[
  {
    "id": "type-uuid",
    "name": "Development",
    "displayOrder": 1,
    "isActive": true
  }
]

Get All Activity Types

Get all activity types including inactive.

GET /activitytypes/all

Create Activity Type

POST /activitytypes
Content-Type: application/json

Request Body

{
  "name": "Bug Triage",
  "displayOrder": 5,
  "isActive": true
}

Update Activity Type

PUT /activitytypes/{id}
Content-Type: application/json

Delete Activity Type

DELETE /activitytypes/{id}

License / Subscription

Get Subscription Status

GET /license/status

Response

{
  "userCount": 8,
  "freeTierLimit": 5,
  "isFreeTier": false,
  "hasActiveSubscription": true,
  "requiresSubscription": false,
  "status": "active",
  "subscriptionId": "sub_xxx",
  "seatCount": 10,
  "message": null,
  "managementUrl": "https://paddle.com/..."
}

Refresh Subscription

Sync subscription status from Paddle.

POST /license/refresh

Create Checkout Session

Create a secure Paddle checkout session for subscription purchase.

POST /subscription/checkout
Content-Type: application/json

Request Body

{
  "seats": 10
}

Response

{
  "checkoutUrl": "https://checkout.paddle.com/...",
  "transactionId": "txn_xxx"
}

Update Seats

Update seat count on an existing subscription.

POST /subscription/update-seats
Content-Type: application/json

Request Body

{
  "seats": 15
}

Response

{
  "success": true,
  "newSeatCount": 15
}

Get Subscription Portal

Get the Paddle customer portal URL for subscription management.

POST /subscription/portal

Response

{
  "portalUrl": "https://customer-portal.paddle.com/..."
}

Health Check

Anonymous health check endpoint.

GET /license/health

Response

{
  "status": "healthy",
  "timestamp": "2025-01-15T10:30:00Z"
}

Distinct Values

Get distinct values for filter dropdowns.

Get Distinct Users

GET /effortlogs/distinct/users

Get Distinct Projects

GET /effortlogs/distinct/projects

Get Distinct Activity Types

GET /effortlogs/distinct/activitytypes

Get Distinct Areas

GET /effortlogs/distinct/areas

Organization Admin

Get Organization Stats

GET /org-admin/stats

Response

{
  "totalRecords": 1250,
  "totalHours": 4832.5,
  "distinctUsers": 12,
  "projects": ["ProjectA", "ProjectB"],
  "projectCount": 2
}

Get Organization Users

GET /org-admin/users

Response

[
  {
    "userId": "user-guid",
    "displayName": "John Doe"
  }
]

Export All Organization Data

POST /org-admin/export

Response: ZIP file with CSV data


Permissions

Get User Permissions

Check the current user's admin permissions.

GET /permissions

Response

{
  "isProjectAdmin": true,
  "isOrgAdmin": false
}

Permission Logic:

  • isProjectAdmin: User is member of [ProjectName]\Project Administrators
  • isOrgAdmin: User is member of [OrgName]\Project Collection Administrators
  • Org Admins automatically have Project Admin permissions

Caching: Permissions are cached for 5 minutes to reduce API calls.


Initialization

Initialize Organization

Initialize tables and seed default activity types for a new organization.

POST /init

Response

{
  "success": true,
  "message": "Organization initialized successfully"
}

Notes:

  • Creates organization tables: EffortLogs, ActivityTypes
  • Creates global Subscriptions table
  • Seeds default activity types (Development, Testing, Documentation, etc.)
  • Safe to call multiple times (idempotent)

Error Responses

400 Bad Request

Invalid request parameters.

{
  "error": "Validation failed",
  "details": ["Hours must be greater than 0"]
}

401 Unauthorized

Missing or invalid authentication.

{
  "error": "Unauthorized"
}

403 Forbidden

License limit exceeded.

{
  "error": "License limit reached",
  "message": "Your organization has 8 users. A subscription is required for more than 5 users."
}

404 Not Found

Resource not found.

{
  "error": "Effort log not found"
}

500 Internal Server Error

Server error.

{
  "error": "An error occurred processing your request"
}

503 Service Unavailable

Azure Table Storage is temporarily unavailable (cooldown period after table deletion).

{
  "error": "Table EffortLogs is temporarily unavailable (may have been recently deleted). Please wait 30 seconds and try again."
}

Important: This occurs when tables are deleted and recreated (e.g., during purge operations). Azure Table Storage has a ~30 second soft-delete cooldown period. Simply wait 30 seconds and retry the request.


Rate Limiting

The internal API (used by the extension) has no rate limiting.

The Public API v1 (/api/v1/*) enforces per-token and per-organization rate limits:

Tier Limit Applies To
Standard per-minute 30/min per token All non-export endpoints
Standard per-hour 250/hour per token All non-export endpoints
Export per-hour 10/hour per token /export endpoints only
Org-wide per-minute 100/min per org All endpoints

Rate limit headers are included in all v1 responses:

  • X-RateLimit-Remaining — Remaining requests in current window
  • X-RateLimit-Reset — Unix timestamp when the window resets

When limits are exceeded, the API returns 429 Too Many Requests:

{
  "error": "Rate limit exceeded. Please retry after the Retry-After period."
}

The Retry-After header indicates how many seconds to wait.


Webhooks (Paddle)

Effort Tracker receives subscription events from Paddle:

POST /paddle/webhook

This endpoint is for Paddle's use only and requires signature verification.


Public API v1

The Public API provides read-only access to effort data for external integrations, dashboards, and automation.

Base URL:

https://efforttrackerapi.sinaware.com/api/v1

Swagger UI: https://efforttrackerapi.sinaware.com/api/swagger/ui

OpenAPI Spec: https://efforttrackerapi.sinaware.com/api/swagger.json

Authentication

All Public API requests require the same authentication as the internal API:

Header Required Description
Authorization Yes Bearer {Azure DevOps PAT}
X-Organization Yes Azure DevOps organization name

Response Envelope

All v1 responses use a standard envelope:

{
  "data": ...,
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalCount": 123,
    "totalPages": 3
  },
  "meta": {
    "requestedAt": "2026-05-12T00:00:00+00:00"
  }
}
  • data — The response payload (array or object depending on endpoint)
  • pagination — Present only on paginated endpoints, null otherwise
  • meta — Request metadata (always present)

Get Effort Logs

Retrieve effort logs for a specific work item.

GET /v1/effortlogs?workItemId={id}&page={page}&pageSize={pageSize}

Query Parameters

Parameter Type Required Default Max
workItemId integer Yes
page integer No 1
pageSize integer No 50 500

Response — Paginated array of effort logs.

{
  "data": [
    {
      "id": "abc123",
      "workItemId": 42,
      "date": "2026-05-01",
      "hours": 4.0,
      "activityType": "Development",
      "notes": "Implemented feature X",
      "userId": "user-guid",
      "userDisplayName": "Alice Johnson",
      "projectName": "MyProject",
      "areaPath": "MyProject\\Backend",
      "createdAt": "2026-05-01T10:00:00+00:00"
    }
  ],
  "pagination": { "page": 1, "pageSize": 50, "totalCount": 1, "totalPages": 1 },
  "meta": { "requestedAt": "2026-05-12T00:00:00+00:00" }
}

Query Effort Logs

Query effort logs with advanced filters.

POST /v1/effortlogs/query

Request Body

{
  "dateFrom": "2026-01-01",
  "dateTo": "2026-03-31",
  "usernames": ["Alice Johnson"],
  "areas": ["MyProject\\Backend"],
  "workItemIds": [123, 456],
  "activityTypes": ["Development"],
  "projects": ["MyProject"],
  "notes": "search text",
  "page": 1,
  "pageSize": 50,
  "sortBy": "date",
  "sortDesc": true
}

All filter fields are optional. If no filters are provided, all logs are returned (paginated).

Field Type Description
dateFrom string Start date (YYYY-MM-DD)
dateTo string End date (YYYY-MM-DD)
usernames string[] Filter by display names
areas string[] Filter by area paths
workItemIds int[] Filter by work item IDs
activityTypes string[] Filter by activity type names
projects string[] Filter by project names
notes string Search text in notes (contains)
page integer Page number (default: 1)
pageSize integer Results per page (default: 50, max: 500)
sortBy string Sort field: date, hours, user, activityType
sortDesc boolean Sort descending (default: false)

Response — Same format as Get Effort Logs (paginated array).


Export Effort Logs

Export effort logs as CSV or JSON.

GET /v1/effortlogs/export?format={format}&dateFrom={date}&dateTo={date}

Query Parameters

Parameter Type Required Default Description
format string No json csv or json
dateFrom string No Start date (YYYY-MM-DD)
dateTo string No End date (YYYY-MM-DD)
usernames string No Comma-separated user names
areas string No Comma-separated area paths
activityTypes string No Comma-separated activity types
projects string No Comma-separated project names

JSON Response — Envelope with data array (no pagination).

CSV Response — Raw CSV file with columns: Id, WorkItemId, Date, Hours, ActivityType, Notes, UserId, UserDisplayName, ProjectName, AreaPath, CreatedAt.

Note: Export endpoints have a separate rate limit of 10 requests per hour.


Get Activity Types

Retrieve active activity types.

GET /v1/activitytypes?project={projectName}

Query Parameters

Parameter Type Required Description
project string No Filter by project name

Response

{
  "data": [
    {
      "id": "type-id",
      "name": "Development",
      "isActive": true,
      "displayOrder": 1
    }
  ],
  "meta": { "requestedAt": "2026-05-12T00:00:00+00:00" }
}

Get Statistics Summary

Get organization-wide statistics.

GET /v1/stats/summary

Response

{
  "data": {
    "totalRecords": 150,
    "totalHours": 425.5,
    "userCount": 8,
    "projectCount": 3
  },
  "meta": { "requestedAt": "2026-05-12T00:00:00+00:00" }
}

Get User Counts

Get effort log counts per user.

GET /v1/stats/user-counts?project={projectName}

Query Parameters

Parameter Type Required Description
project string No Filter by project name

Response

{
  "data": [
    { "user": "Alice Johnson", "count": 45 },
    { "user": "Bob Smith", "count": 32 }
  ],
  "meta": { "requestedAt": "2026-05-12T00:00:00+00:00" }
}

Effort Tracker Wiki

Getting Started

User Guides

Administration

Reference


Links

Clone this wiki locally