Getting Started with the InLoox API
This guide helps you set up authentication, understand OData basics, and send your first API request.
Prerequisites
Before you begin, you need:
- An active InLoox account (Cloud or Self-Hosted)
- An HTTP client like cURL, Postman, or a programming language of your choice
- A valid Personal Access Token (PAT) — See Create Personal Access Token for step-by-step instructions.
Your Personal Access Token grants full API access on your behalf. Treat it like a password — never commit it to version control and do not share it publicly.
A token is as powerful as the user it belongs to. For production environments, it is recommended to create a dedicated service user account with permissions restricted to the minimum required scope.
Authentication
The InLoox API uses Personal Access Tokens for authentication. Include your token with every request as an x-api-key HTTP header.
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);
:::tip Verify Your Token
A quick way to test your token is to call the AccountInfo endpoint:
curl -X GET "https://app.inloox.com/api/odata/AccountInfo" \
-H "x-api-key: YOUR_API_TOKEN"
On successful authentication, you will receive the account information, including the AccountId and account name. :::
:::tip Example: Retrieve Current User Profile
Use the UserInfo/Me() endpoint to retrieve the profile of the currently authenticated user:
curl -X GET "https://app.inloox.com/api/odata/UserInfo/Me()" \
-H "x-api-key: YOUR_API_TOKEN"
Response:
{
"ContactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"FirstName": "John",
"LastName": "Doe",
"DisplayName": "John Doe",
"Email": "john.doe@example.com",
"ImageId": null,
"NotifyFrequency": 1,
"TimeZoneInfo": "W. Europe Standard Time",
"EmailConfirmed": true,
"IsReader": false,
"UILanguage": "en-US"
}
:::
:::warning Note
To use the API with full read and write access, your account must have "EmailConfirmed": true and "IsReader": false. Users with an unconfirmed email address or a Read-Only license (IsReader: true) have limited API access.
:::
Base URL
All API requests are made against a base URL that depends on your deployment:
| Deployment | Base URL |
|---|---|
| InLoox Cloud | https://app.inloox.com/api/ |
| InLoox Self-Hosted | https://YOUR-SELF-HOSTED-URL/api/v1/ |
All endpoint paths in this documentation are relative to the base URL. For example:
GET /odata/Project
means the full URL for the Cloud is https://app.inloox.com/api/odata/Project.
OData Basics
The InLoox API is based on the OData protocol (Open Data Protocol). OData provides a standardized query language that allows you to filter, sort, select, and paginate data — directly via the URL.
What is OData?
OData is an open standard for building and consuming REST APIs. It defines proven patterns for queries controlled through URL parameters ($filter, $select, $orderby, etc.). This allows you to precisely control what data you receive without requiring special endpoints.
Supported HTTP Methods
| Method | Usage |
|---|---|
GET | Read entities or collections |
POST | Create new entities or invoke actions |
PATCH | Update existing entities (partial update) |
DELETE | Delete entities |
Query Options
OData supports powerful query options as URL parameters. These allow you to filter, sort, paginate, and shape the data returned by the API.
$filter - Filter Data
Use $filter to restrict the returned records based on conditions.
Filter projects by name:
curl -X GET "https://app.inloox.com/api/odata/Project?\$filter=Name eq 'My Project'" \
-H "x-api-key: YOUR_API_TOKEN"
Projects created after a specific date:
curl -X GET "https://app.inloox.com/api/odata/Project?\$filter=CreatedDateTime gt 2024-01-01T00:00:00Z" \
-H "x-api-key: YOUR_API_TOKEN"
Combine multiple conditions (and / or):
curl -X GET "https://app.inloox.com/api/odata/Project?\$filter=IsArchived eq false and Name ne 'Archived'" \
-H "x-api-key: YOUR_API_TOKEN"
Common filter operators:
| Operator | Meaning | Example |
|---|---|---|
eq | Equal | Name eq 'Test' |
ne | Not equal | Status ne 'Closed' |
gt | Greater than | CreatedDateTime gt 2024-01-01T00:00:00Z |
lt | Less than | Progress lt 50 |
ge | Greater than or equal | Progress ge 80 |
le | Less than or equal | Progress le 100 |
and | Logical AND | IsArchived eq false and Progress gt 0 |
or | Logical OR | Status eq 'Open' or Status eq 'InProgress' |
contains() | Contains | contains(Name, 'Marketing') |
startswith() | Starts with | startswith(Name, 'Project') |
:::note String Values in Filters
String values must be enclosed in single quotes: Name eq 'My Project'. GUIDs and dates do not require quotes.
:::
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Filter(p => p.Name == "Website Redesign")
.FindEntriesAsync();
$select - Select Fields
Use $select to specify which properties are returned in the response. This reduces the payload size and improves performance.
curl -X GET "https://app.inloox.com/api/odata/Project?\$select=ProjectId,Name,StartDate,EndDate" \
-H "x-api-key: YOUR_API_TOKEN"
C# example:
var response = await client.GetAsync("Project?$select=ProjectId,Name,StartDate");
// C# with Simple.OData.Client
var projects = await client
.For<ApiProject>("Project")
.Select(p => new { p.ProjectId, p.Name })
.FindEntriesAsync();
$orderby - Sorting
Use $orderby to sort results by one or more properties.
Sort ascending (default):
curl -X GET "https://app.inloox.com/api/odata/Project?\$orderby=Name" \
-H "x-api-key: YOUR_API_TOKEN"
Sort descending:
curl -X GET "https://app.inloox.com/api/odata/Project?\$orderby=CreatedDateTime desc" \
-H "x-api-key: YOUR_API_TOKEN"
Multiple sort criteria:
curl -X GET "https://app.inloox.com/api/odata/Project?\$orderby=IsArchived desc,Name asc" \
-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 and $skip to retrieve results page by page.
| Parameter | Description | Example |
|---|---|---|
$top | Maximum number of entries returned | $top=20 |
$skip | Number of entries to skip | $skip=40 |
First 20 projects:
curl -X GET "https://app.inloox.com/api/odata/Project?\$top=20" \
-H "x-api-key: YOUR_API_TOKEN"
Page 3 (items 41–60):
curl -X GET "https://app.inloox.com/api/odata/Project?\$top=20&\$skip=40" \
-H "x-api-key: YOUR_API_TOKEN"
// C# with Simple.OData.Client
var page2 = await client
.For<ApiProject>("Project")
.Skip(10)
.Top(10)
.FindEntriesAsync();
Combining Query Options
You can combine multiple query options in a single request:
# Top 5 open tasks in a project, sorted by due date, key fields only
curl "https://app.inloox.com/api/odata/Task?\$filter=ProjectId eq {projectId} and IsDone eq false&\$orderby=EndDateTime asc&\$top=5&\$select=TaskItemId,Name,EndDateTime" \
-H "x-api-key: YOUR_API_TOKEN"
Pagination
The API returns a maximum of 100 items per request by default. To retrieve all records, you need to paginate through the pages.
Pagination Strategy
- Send a request with
$top - Check whether the response contains an
@odata.nextLinkfield - If so, send the next request to the URL specified in
@odata.nextLink - Repeat until no
@odata.nextLinkis present
How Pagination Works
When more results are available, the response contains an @odata.nextLink property with 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 Pagination with $top and $skip
# Page 1 (entries 1–100)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=0" \
-H "x-api-key: YOUR_API_TOKEN"
# Page 2 (entries 101–200)
curl "https://app.inloox.com/api/odata/Project?\$top=100&\$skip=100" \
-H "x-api-key: YOUR_API_TOKEN"
# Page 3 (entries 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.");
Pagination with Simple.OData.Client (C#)
The ODataFeedAnnotations class handles pagination 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}");
:::warning Performance Note
When retrieving large datasets, use $filter to narrow the query instead of 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 this:
{
"@odata.context": "https://app.inloox.com/api/odata/$metadata#Project",
"value": [
{
"ProjectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Name": "Website Relaunch",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z"
}
]
}
Key elements:
| Property | Description |
|---|---|
@odata.context | URL to the entity metadata schema |
value | Array of entities (for collections) |
@odata.nextLink | URL of the next page (when more data is available) |
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 Relaunch",
"StartDate": "2024-01-15T00:00:00Z",
"EndDate": "2024-06-30T00:00:00Z"
}
Error Handling
The API uses standard HTTP status codes to indicate the success or failure of a request.
| Status Code | Meaning | Description |
|---|---|---|
200 OK | Success | The request was successful. |
201 Created | Created | A new resource was successfully created. |
204 No Content | No Content | The request was successful, there is no return data (e.g., for DELETE). |
400 Bad Request | Bad Request | The request is invalid — check the parameters and request body. |
401 Unauthorized | Unauthorized | The API token is missing or invalid. |
403 Forbidden | Forbidden | You do not have permission for this resource. |
404 Not Found | Not Found | The requested resource does not exist. |
429 Too Many Requests | Rate Limited | Too many requests in a short period. |
500 Internal Server Error | Server Error | An internal error occurred — contact support. |
Error Response Example
{
"error": {
"code": "400",
"message": "The query specified in the URI is not valid. Could not find a property named 'InvalidField' on type 'ApiProject'."
}
}
:::tip Troubleshooting Tips
- 401 errors: Check whether the
x-api-keyheader is set correctly and the token is valid. - 400 errors: Verify the syntax of your OData query and field names.
- 404 errors: Ensure the base URL and endpoint path are correct.
- Check the spelling of property names in
$select,$filter, and$orderbyif needed — they are case-sensitive. - Use the
$metadataendpoint to inspect available entities and properties:GET https://app.inloox.com/api/odata/$metadata
:::
Rate Limits
The InLoox API does not publish strict rate limits, but follow these best practices to avoid throttling:
- Batch read operations — Use
$filterand$selectto minimize the number of requests - Do not poll aggressively — Wait at least a few seconds between repeated requests
- Paginate efficiently — Follow
@odata.nextLinkinstead of creating overlapping queries - Use caching — Cache results locally when data does not change frequently
If you receive a 429 Too Many Requests response, wait for the duration specified in the Retry-After header before retrying.
Next Steps
You are ready to get started! Here is where to go next:
- Code Examples — Complete, working C# examples to clone and run
- Projects — Work with the
Projectentity - Tasks — Query and manage tasks
- Time Entries — Record and retrieve time tracking data
- Documentation MCP — Connect AI tools to InLoox docs via MCP