API Integration
This document covers how to integrate with OctoMesh APIs, including authentication, GraphQL operations, and the service client SDK.
API Overview
OctoMesh provides multiple API access methods:
| API Type | Protocol | Use Case |
|---|---|---|
| GraphQL | HTTPS | Primary data access, queries, mutations |
| REST | HTTPS | Administrative operations, file uploads |
| AMQP | RabbitMQ | Real-time messaging, adapter communication |
Authentication
OAuth 2.0 / OpenID Connect
OctoMesh uses OAuth 2.0 with OpenID Connect for authentication.
Obtaining Access Token
Token Request:
POST /connect/token HTTP/1.1
Host: identity.octomesh.local
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=my-application
&client_secret=my-secret
&scope=octomesh.read octomesh.write
Token Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "octomesh.read octomesh.write"
}
API Scopes
| Scope | Description |
|---|---|
octomesh.read | Read access to runtime and stream data |
octomesh.write | Write access to runtime data |
octomesh.admin | Administrative operations |
octomesh.ck.read | Read Construction Kit definitions |
octomesh.ck.write | Modify Construction Kit definitions |
Using the Token
Include the token in the Authorization header:
GET /tenants/my-tenant/graphql HTTP/1.1
Host: asset-repo.octomesh.local
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
GraphQL API
Endpoint
https://{asset-repo-host}/tenants/{tenantId}/graphql
GraphQL Playground (interactive explorer):
https://{asset-repo-host}/tenants/{tenantId}/graphql/playground
Query Structure
The GraphQL schema is organized into two main query roots:
type Query {
runtime: RuntimeQuery # Master data (MongoDB)
streamData: StreamQuery # Time-series data (CrateDB)
}
type Mutation {
runtime: RuntimeMutation
}
Runtime Queries
Query all entities of a type:
query GetEnergyMeters {
runtime {
rtIndustryEnergyEnergyMeter(
first: 50
after: "cursor"
filter: {
status: { eq: "Active" }
}
orderBy: { name: ASC }
) {
items {
rtId
ckTypeId
wellKnownName
name
serialNumber
status
location {
items {
rtId
name
address
}
}
}
pageInfo {
hasNextPage
endCursor
totalCount
}
}
}
}
Query by ID:
query GetMeterById($rtId: OctoObjectId!) {
runtime {
rtIndustryEnergyEnergyMeterById(rtId: $rtId) {
rtId
name
serialNumber
readings(first: 10) {
items {
timestamp
value
}
}
}
}
}
Query by wellKnownName:
query GetMeterByName {
runtime {
rtIndustryEnergyEnergyMeterByWellKnownName(
wellKnownName: "main-building-meter"
) {
rtId
name
}
}
}
Stream Data Queries
Query time-series data:
query GetMeterReadings($rtId: OctoObjectId!) {
streamData {
tsIndustryEnergyMeterReading(
first: 1000
filter: {
rtId: { eq: $rtId }
timestamp: {
gte: "2024-01-01T00:00:00Z"
lte: "2024-01-31T23:59:59Z"
}
}
orderBy: { timestamp: DESC }
) {
items {
rtId
timestamp
voltage
current
power
energy
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Mutations
Create entity:
mutation CreateEnergyMeter {
runtime {
createRtIndustryEnergyEnergyMeter(
entity: {
wellKnownName: "new-meter-001"
attributes: {
name: "New Energy Meter"
serialNumber: "EM-2024-001"
manufacturer: "Siemens"
status: Active
}
}
) {
rtId
wellKnownName
}
}
}
Update entity:
mutation UpdateEnergyMeter($rtId: OctoObjectId!) {
runtime {
updateRtIndustryEnergyEnergyMeter(
rtId: $rtId
entity: {
attributes: {
status: Maintenance
}
}
) {
rtId
status
}
}
}
Delete entity:
mutation DeleteEnergyMeter($rtId: OctoObjectId!) {
runtime {
deleteRtIndustryEnergyEnergyMeter(rtId: $rtId)
}
}
Manage associations:
mutation AddMeterToLocation($meterId: OctoObjectId!, $locationId: OctoObjectId!) {
runtime {
addAssociationRtIndustryEnergyEnergyMeter(
rtId: $meterId
association: "location"
targetRtIds: [$locationId]
) {
rtId
location {
items {
rtId
}
}
}
}
}
Filtering
OctoMesh GraphQL supports rich filtering:
query FilteredQuery {
runtime {
rtIndustryEnergyEnergyMeter(
filter: {
AND: [
{ status: { eq: "Active" } }
{
OR: [
{ manufacturer: { contains: "Siemens" } }
{ manufacturer: { contains: "ABB" } }
]
}
{ maxCapacity: { gte: 100 } }
]
}
) {
items {
rtId
name
}
}
}
}
Available filter operators:
| Operator | Description |
|---|---|
eq | Equals |
neq | Not equals |
gt / gte | Greater than / Greater than or equal |
lt / lte | Less than / Less than or equal |
contains | String contains |
startsWith | String starts with |
endsWith | String ends with |
in | Value in list |
AND / OR | Logical operators |
Service Client SDK
The .NET SDK provides a typed client for OctoMesh APIs.
Installation
dotnet add package Meshmakers.Octo.Sdk.ServiceClient
Configuration
using Meshmakers.Octo.Sdk.ServiceClient;
var services = new ServiceCollection();
services.AddOctoMeshServiceClient(options =>
{
options.IdentityServiceUrl = "https://identity.octomesh.local";
options.AssetRepositoryUrl = "https://asset-repo.octomesh.local";
options.ClientId = "my-application";
options.ClientSecret = "my-secret";
options.TenantId = "my-tenant";
});
Using the Service Client
public class MyService
{
private readonly IAssetRepositoryClient _client;
public MyService(IAssetRepositoryClient client)
{
_client = client;
}
public async Task<IEnumerable<EnergyMeter>> GetActiveMetersAsync(
CancellationToken cancellationToken)
{
var query = new GraphQLRequest
{
Query = @"
query {
runtime {
rtIndustryEnergyEnergyMeter(
filter: { status: { eq: ""Active"" } }
) {
items {
rtId
name
serialNumber
}
}
}
}"
};
var response = await _client.ExecuteGraphQLAsync<EnergyMeterResponse>(
query, cancellationToken);
return response.Runtime.RtIndustryEnergyEnergyMeter.Items;
}
public async Task<string> CreateMeterAsync(
CreateMeterRequest request,
CancellationToken cancellationToken)
{
var mutation = new GraphQLRequest
{
Query = @"
mutation CreateMeter($input: CreateEnergyMeterInput!) {
runtime {
createRtIndustryEnergyEnergyMeter(entity: $input) {
rtId
}
}
}",
Variables = new { input = request }
};
var response = await _client.ExecuteGraphQLAsync<CreateMeterResponse>(
mutation, cancellationToken);
return response.Runtime.CreateRtIndustryEnergyEnergyMeter.RtId;
}
}
Source Generation
Use source generation to create typed DTOs:
dotnet add package Meshmakers.Octo.Sdk.SourceGeneration
// Define your models with source generation attributes
[GenerateGraphQLTypes("Industry.Energy/EnergyMeter")]
public partial class EnergyMeterDto
{
}
// Generated code provides:
// - Strongly typed properties matching CK attributes
// - Serialization support
// - GraphQL query/mutation helpers
REST API
Administrative Endpoints
Get tenant information:
GET /api/tenants/{tenantId}
Authorization: Bearer {token}
Upload file:
POST /api/tenants/{tenantId}/files
Authorization: Bearer {token}
Content-Type: multipart/form-data
file=@document.pdf
Get system health:
GET /health
Construction Kit Management
List CK libraries:
GET /api/tenants/{tenantId}/construction-kits
Authorization: Bearer {token}
Upload CK library:
POST /api/tenants/{tenantId}/construction-kits
Authorization: Bearer {token}
Content-Type: application/json
{
"libraryId": "MyCompany.Domain",
"version": "1.0.0",
"types": [...],
"enums": [...]
}
Error Handling
GraphQL Errors
GraphQL errors are returned in the errors array:
{
"data": null,
"errors": [
{
"message": "Entity not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["runtime", "rtIndustryEnergyEnergyMeterById"],
"extensions": {
"code": "ENTITY_NOT_FOUND",
"details": {
"rtId": "invalid-id"
}
}
}
]
}
Common Error Codes
| Code | Description |
|---|---|
UNAUTHORIZED | Missing or invalid token |
FORBIDDEN | Insufficient permissions |
ENTITY_NOT_FOUND | Requested entity does not exist |
VALIDATION_ERROR | Input validation failed |
CK_TYPE_NOT_FOUND | Construction Kit type not found |
ASSOCIATION_ERROR | Invalid association operation |
Client-Side Error Handling
try
{
var result = await _client.ExecuteGraphQLAsync<Response>(query, ct);
if (result.Errors?.Any() == true)
{
foreach (var error in result.Errors)
{
_logger.LogError("GraphQL error: {Message} (Code: {Code})",
error.Message,
error.Extensions?["code"]);
}
throw new OctoMeshException("GraphQL operation failed", result.Errors);
}
return result.Data;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP request failed");
throw;
}
catch (AuthenticationException ex)
{
_logger.LogError(ex, "Authentication failed");
// Refresh token and retry
throw;
}
Rate Limiting
OctoMesh implements rate limiting to protect the platform:
| Endpoint Type | Limit |
|---|---|
| GraphQL queries | 100 req/min per client |
| Mutations | 50 req/min per client |
| File uploads | 10 req/min per client |
When rate limited, you'll receive:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Best Practices
Query Optimization
- Request only needed fields: Reduce response size
- Use pagination: Always set
firstparameter - Filter server-side: Don't fetch and filter client-side
- Batch related queries: Combine in single request
Connection Management
- Reuse HTTP clients: Use connection pooling
- Handle token refresh: Implement automatic refresh
- Implement retry logic: Handle transient failures
- Use timeouts: Set appropriate request timeouts
Security
- Secure credentials: Use secrets management
- Minimal scopes: Request only needed permissions
- Validate responses: Don't trust external data
- Log securely: Don't log tokens or sensitive data
Next Steps
- SDK Overview: Explore available SDK libraries
- Adapter Development: Build custom integrations
- API Reference: Complete API documentation