Skip to main content

Getting Started with the InLoox API

This guide walks you through everything you need to make your first API request — from authentication to querying data with OData.


Prerequisites

Before you begin, make sure you have:

  • An InLoox account (Cloud or Self-Hosted with API access enabled)
  • A Personal Access Token — you can generate one from your InLoox profile settings under Options → Personal Access Token
  • An HTTP client such as cURL, Postman, or your programming language of choice
Create a Token

Navigate in InLoox to File → Options → Personal Access Tokens and click Create New Key. Copy the generated token — it is only shown once.

Keep Your Token Secret

Your Personal Access Token grants full access to the API on your behalf. Treat it like a password — never commit it to source control or share it publicly.


Authentication

The InLoox API uses token-based authentication via the x-api-key HTTP header. Include this header in every request.

cURL

curl -X GET "https://app.inloox.com/api/odata/Project" \
-H "x-api-key: YOUR_API_TOKEN"

C# (HttpClient)

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_TOKEN");

var response = await client.GetAsync("https://app.inloox.com/api/odata/Project");
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);

C# (Simple.OData.Client)

var settings = new ODataClientSettings(new Uri("https://app.inloox.com/api/odata/"));
settings.BeforeRequest += delegate (HttpRequestMessage message)
{
message.Headers.Add("x-api-key", "YOUR_API_TOKEN");
};
var client = new ODataClient(settings);

JavaScript (Fetch)

const response = await fetch("https://app.inloox.com/api/odata/Project", {
headers: {
"x-api-key": "YOUR_API_TOKEN"
}
});
const data = await response.json();
console.log(data);
Verify Your Token

A quick way to verify your token works is to call the AccountInfo endpoint:

curl -X GET "https://app.inloox.com/api/odata/AccountInfo" \
-H "x-api-key: YOUR_API_TOKEN"

If authentication succeeds, you'll receive your user profile information.


Base URL

All API requests are made against a base URL that depends on your deployment:

DeploymentBase URL
InLoox Cloudhttps://app.inloox.com/api/odata/
InLoox Self-Hostedhttps://YOUR-SELF-HOSTED-URL/api/v1/odata/

All endpoint paths in this documentation are relative to the base URL. For example:

GET /api/odata/Project

means the full URL for Cloud is https://app.inloox.com/api/odata/Project.


OData Basics

The InLoox API follows the OData (Open Data Protocol) standard. OData provides a uniform way to query and manipulate data using standard HTTP methods and URL conventions.

What is OData?

OData is an open standard for building and consuming REST APIs. It defines proven patterns for queries controlled via URL parameters ($filter, $select, $orderby, etc.). This lets you precisely control which data you receive without requiring custom endpoints.

Supported HTTP Methods

MethodUsage
GETRead entities or collections
POSTCreate new entities or invoke actions
PATCHUpdate existing entities (partial update)
DELETEDelete entities

Query Options

OData supports powerful query options as URL parameters. These let you filter, sort, page, and shape the data returned by the API.


$filter — Filter Results

Use $filter to restrict the results to entities that match a condition.

Operators: eq, ne, gt, ge, lt, le, and, or, not

# Projects named "Website Redesign"
curl "https://app.inloox.com/api/odata/Project?\$filter=Name eq 'Website Redesign'" \
-H "x-api-key: YOUR_API_TOKEN"
# Projects started after January 1, 2024
curl "https://app.inloox.com/api/odata/Project?\$filter=StartDate gt 2024-01-01" \
-H "x-api-key: YOUR_API_TOKEN"
# Open tasks in a specific project
curl "https://app.inloox.com/api/odata/Task?\$filter=ProjectId eq {projectId} and IsDone eq false" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Filter(p => p.Name == "Website Redesign")
.FindEntriesAsync();
String Values in Filters

String values must be enclosed in single quotes: Name eq 'My Project'. GUIDs and dates do not need quotes.

Common filter operators:

OperatorMeaningExample
eqEqualsName eq 'Test'
neNot equalsStatus ne 'Closed'
gtGreater thanCreatedDate gt 2024-01-01T00:00:00Z
ltLess thanProgress lt 50
geGreater than or equalProgress ge 80
leLess than or equalProgress le 100
andLogical andIsActive eq true and Progress gt 0
orLogical orStatus eq 'Open' or Status eq 'InProgress'
contains()Containscontains(Name, 'Marketing')
startswith()Starts withstartswith(Name, 'Project')

$select — Choose Specific Properties

Use $select to return only the properties you need. This reduces response size and improves performance.

# Return only project ID and name
curl "https://app.inloox.com/api/odata/Project?\$select=ProjectId,Name" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Select(p => new { p.ProjectId, p.Name })
.FindEntriesAsync();

$orderby — Sort Results

Use $orderby to sort results by one or more properties. Append asc (default) or desc for sort direction.

# Projects sorted by name ascending
curl "https://app.inloox.com/api/odata/Project?\$orderby=Name asc" \
-H "x-api-key: YOUR_API_TOKEN"

# Projects sorted by start date descending
curl "https://app.inloox.com/api/odata/Project?\$orderby=StartDate desc" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.OrderBy(p => p.StartDate)
.FindEntriesAsync();

$top and $skip — Paging

Use $top to limit the number of results and $skip to offset into the collection. Together, they enable paging.

# First 10 projects
curl "https://app.inloox.com/api/odata/Project?\$top=10" \
-H "x-api-key: YOUR_API_TOKEN"

# Projects 11–20 (page 2 with page size 10)
curl "https://app.inloox.com/api/odata/Project?\$top=10&\$skip=10" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var page2 = await client
.For<ApiProject>("Project")
.Skip(10)
.Top(10)
.FindEntriesAsync();

$count — Get Total Count

Use $count to get the total number of entities that match your query.

# Total number of projects
curl "https://app.inloox.com/api/odata/Project/\$count" \
-H "x-api-key: YOUR_API_TOKEN"

You can also combine $count=true with a regular query to include the count in the response alongside the data:

curl "https://app.inloox.com/api/odata/Project?\$count=true&\$top=10" \
-H "x-api-key: YOUR_API_TOKEN"

Where supported, use $expand to include related entities inline rather than making separate requests.

curl "https://app.inloox.com/api/odata/Task?\$expand=Project" \
-H "x-api-key: YOUR_API_TOKEN"
info

Not all entities support $expand. Check the specific entity documentation for supported expansions.


Combining Query Options

You can combine multiple query options in a single request:

# Top 5 open tasks in a project, sorted by due date, returning only key fields
curl "https://app.inloox.com/api/odata/Task?\$filter=ProjectId eq {projectId} and IsDone eq false&\$orderby=EndDate asc&\$top=5&\$select=TaskItemId,DisplayName,EndDate" \
-H "x-api-key: YOUR_API_TOKEN"

Paging

The API returns a maximum of 100 items per request by default. If your query matches more than 100 items, you must paginate through the results.

Paging Strategy

  1. Send a request with $top and optionally $count=true
  2. Check if the response contains an @odata.nextLink field
  3. If yes, send the next request to the URL specified in @odata.nextLink
  4. Repeat until no @odata.nextLink is present

How Paging Works

When there are more results available, the response includes a @odata.nextLink property containing the URL for the next page:

{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project",
"value": [ ... ],
"@odata.nextLink": "https://app.inloox.com/api/odata/Project?$skip=100"
}

Manual Paging with $top and $skip

# Page 1 (items 1–100)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=0" \
-H "x-api-key: YOUR_API_TOKEN"

# Page 2 (items 101–200)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=100" \
-H "x-api-key: YOUR_API_TOKEN"

# Page 3 (items 201–300)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=200" \
-H "x-api-key: YOUR_API_TOKEN"

Manual Paging Example (C#)

var allProjects = new List<ApiProject>();
int skip = 0;
const int pageSize = 100;

while (true)
{
var response = await client.GetAsync($"Project?$top={pageSize}&$skip={skip}");
var json = await response.Content.ReadAsStringAsync();
var page = JsonSerializer.Deserialize<ODataResponse<ApiProject>>(json);

allProjects.AddRange(page.Value);

if (page.Value.Count < pageSize)
break;

skip += pageSize;
}

Console.WriteLine($"Total {allProjects.Count} projects loaded.");

Paging with Simple.OData.Client (C#)

The ODataFeedAnnotations class handles paging automatically:

var allEntries = new List<ApiProject>();
var annotations = new ODataFeedAnnotations();

var page = (await client
.For<ApiProject>("Project")
.FindEntriesAsync(annotations)).ToList();

allEntries.AddRange(page);

while (annotations.NextPageLink != null)
{
page = (await client
.For<ApiProject>("Project")
.FindEntriesAsync(annotations.NextPageLink, annotations)).ToList();
allEntries.AddRange(page);
}

Console.WriteLine($"Total projects loaded: {allEntries.Count}");
Performance Consideration

When fetching large datasets, consider using $filter to narrow your query rather than paginating through all records. This reduces both network traffic and server load.


Response Format

All API responses use JSON with OData metadata annotations. A typical collection response looks like:

{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project",
"value": [
{
"ProjectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Name": "Website Redesign",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z",
"IsClosed": false
}
]
}

Key elements:

PropertyDescription
@odata.contextURL to the entity metadata schema
valueArray of entities (for collections)
@odata.nextLinkURL of the next page (if more data is available)
@odata.countTotal count of matching entities (when $count=true)

A single-entity response (e.g., GET Project({key})) returns the entity directly without the value wrapper:

{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project/$entity",
"ProjectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Name": "Website Redesign",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z",
"IsClosed": false
}

Error Handling

The API uses standard HTTP status codes. Handle these in your integration:

Status CodeMeaningTypical Cause
200 OKSuccessRequest processed successfully
201 CreatedCreatedEntity created via POST
204 No ContentSuccessUpdate or delete completed
400 Bad RequestClient errorMalformed request, invalid filter syntax, or missing required fields
401 UnauthorizedAuthentication failedMissing or invalid x-api-key header
403 ForbiddenAccess deniedToken is valid but user lacks permission for this operation
404 Not FoundResource not foundEntity ID doesn't exist or endpoint is incorrect
429 Too Many RequestsRate limitedToo many requests in a short period
500 Internal Server ErrorServer errorUnexpected server-side issue — contact InLoox support

Error Response Format

Error responses include a JSON body with details:

{
"error": {
"code": "400",
"message": "The query specified in the URI is not valid. Syntax error at position 15."
}
}
Debugging Tips
  • Double-check your $filter syntax — OData filter expressions are strict about quoting and operator usage.
  • Verify that property names in $select, $filter, and $orderby are case-sensitive and match the API schema.
  • Use the $metadata endpoint to inspect available entities and properties:
    GET https://app.inloox.com/api/odata/$metadata

Rate Limits

While the InLoox API does not publish strict rate limits, follow these best practices to avoid throttling:

  • Batch your reads — use $filter and $select to minimize the number of requests
  • Don't poll aggressively — wait at least a few seconds between repeated requests
  • Paginate efficiently — follow @odata.nextLink instead of making overlapping queries
  • Cache when appropriate — store results locally if the data doesn't change frequently
note

If you receive a 429 Too Many Requests response, wait for the duration indicated in the Retry-After header before retrying.


Next Steps

You're ready to start building! Here's where to go next:

  • Code Examples — Complete working C# examples you can clone and run
  • Projects — Learn to work with the Project entity
  • Tasks — Query and manage tasks
  • Time Entries — Record and retrieve time tracking data