GettingStarted

Dokumentation på svenska: Getting Started

The Nordomatic GraphQL API provides secure access to real-time industrial data and historical analytics.
This modern API enables you to:

  • Query real-time data: Access live values and system status
  • Retrieve historical data: Fetch historical data for trend analysis
  • Manage authentication: Secure token-based access control
  • Subscribe to live streams: Real-time data updates via GraphQL subscriptions

Prerequisites

  • Valid Styrportalen account credentials (provided by your account manager)
  • API endpoint URL (specific to your installation)
  • Basic knowledge of GraphQL (helpful but not required)

API Architecture

Our GraphQL API follows industry best practices with a clean, intuitive structure:

  • Queries: Retrieve data (no "get" prefix needed)
  • Mutations: Modify data with descriptive operation prefixes
  • Subscriptions: Real-time data streaming
  • Comprehensive documentation: Every operation includes examples

Introspection

Introspection is available after sign-in.
Sign-in command examples can be found below in Code Examples

After successful signin, you can use introspection or download full schema.

API Access

  • Endpoint: Your server-specific URL (provided with credentials)
  • Interactive Playground: Built-in GraphQL explorer for testing queries
  • Content Type: application/json
  • Protocol: HTTPS only for security

Mockup server

A mockup server for testing and development is accessible without login at styrportalen.se/6187B2/graphql
That server has all queries and mutations as the production server but return simple "Hello world" objects.

Authentication

User Login (Sign In)

Start by authenticating with your Styrportalen credentials to establish a secure session.

Session Details:

  • Duration: 7 days from login
  • Security: Automatic renewal on activity
  • Cookies: HTTP-only session cookies for security

Token Management

Creating Application Tokens

For production applications, create long-lived API tokens instead of using session cookies.

Token Properties:

  • Label: Descriptive name for identification
  • Read-only: Restrict to data access only (recommended)
  • Expiration: Maximum 1 year (read-only) or 3 months (full access)

⚠️ Important Security Notice

API tokens are displayed only once during creation. Save them securely - they cannot be recovered if lost.

Token Management Operations:

  • userTokens: List all your tokens
  • createUserToken: Generate new tokens
  • deleteUserToken: Permanently remove tokens
  • revokeUserToken: Disable specific tokens
  • revokeAllUserTokens: Emergency token revocation

Rate Limits

To ensure optimal performance for all users, API requests are rate-limited:

Rate Limit Policy:

  • Limit: 30 requests per 60-second window
  • Scope: Per ip-address
  • Reset: Rolling window (not fixed intervals)

Rate Limit Headers:
Our API follows industry standards (GitHub, Stripe, Twitter) for rate limit communication:

x-ratelimit-limit: 30
x-ratelimit-remaining: 25
x-ratelimit-reset: 50

Code Examples

Authentication Examples

Initial SignIn

Authenticate using your Nordomatic credentials to establish a secure session:

mutation SignIn($username: String!, $password: String!) {
  signIn(username: $username, password: $password) {
    user {
      username
      name
      email
      createdAt
      updatedAt
    }
  }
}

Variables:

{
  "username": "your-username",
  "password": "your-password"
}

If authentication succeeds, the server returns a session cookie:
Sessions remain active for 7 days.
Response header example.

Set-Cookie: styrportalen.session_token=...; HttpOnly; Secure  

Subsequent requests must include the cookie:

Cookie: styrportalen.session_token=...

Creating Long-Lived Tokens

For production applications, create long-lived tokens instead of using session cookies:

mutation CreateUserToken($input: CreateUserTokenInput!) {
  createUserToken(input: $input) {
    ... on CreatedUserToken {
      id
      readonly
      expiresAt
      label
      token
    }
    ... on UserTokenError {
      errorMessage
    }
  }
}

Variables:

{
  "input": {
    "label": "Production App v1.0",
    "readonly": false,
    "expiresAt": "2024-12-31T23:59:59Z"
  }
}

Token Configuration Options:

  • Label: Descriptive name for easy identification
  • Readonly: true for read-only access, false for full permissions
  • ExpiresAt: Maximum 1 year for readonly tokens, 3 months for write tokens

⚠️ Important Security Notice

API tokens are displayed only once during creation. Save them securely - they cannot be recovered if lost.


Example: TypeScript (using fetch)

const response = await fetch('https://your-api-url/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: `
mutation SignIn($username: String!, $password: String!) {
  signIn(username: $username, password: $password) {
    user {
      username
      name
      email
      createdAt
      updatedAt
    }
  }
}`,
    variables: {
      username: 'your-username',
      password: 'your-password',
    },
  }),
});
const data = await response.json();  
console.log(data);

Example: Python (using requests)

import requests

url = "https://your-api-url/graphql"

query = """
mutation SignIn($username: String!, $password: String!) {
  signIn(username: $username, password: $password) {
    user {
      username
      name
      email
      createdAt
      updatedAt
    }
  }
}
"""

variables = {
    "username": "your-username",
    "password": "your-password",
}

response = requests.post(
    url,
    json={
        "query": query,
        "variables": variables
    }
)

print(response.json())

Example: C# (using HttpClient)

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var client = new HttpClient();
        var url = "https://your-api-url/graphql";

        var query = @"
mutation SignIn($username: String!, $password: String!) {
  signIn(username: $username, password: $password) {
    user {
      username
      name
      email
      createdAt
      updatedAt
    }
  }
}";

        var payload = new
        {
            query = query,
            variables = new
            {
                username = "your-username",
                password = "your-password"
            }
        };

        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(url, content);
        var result = await response.Content.ReadAsStringAsync();

        Console.WriteLine(result);
    }
}

Example: Java (using OkHttp)

import okhttp3.*;

public class GraphQLSignInExample {
  public static void main(String[] args) throws Exception {

    OkHttpClient client = new OkHttpClient();
    String url = "https://your-api-url/graphql";

    String query = """
      mutation SignIn($username: String!, $password: String!) {
        signIn(username: $username, password: $password) {
          user {
            username
            name
            email
            createdAt
            updatedAt
          }
        }
      }
      """;

    String payload = """
      {
        "query": "%s",
        "variables": {
          "username": "your-username",
          "password": "your-password"
        }
      }
      """.formatted(query.replace("\n", "\\n").replace("\"", "\\\""));

    RequestBody body = RequestBody.create(
      payload,
      MediaType.parse("application/json")
    );

    Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

    try (Response response = client.newCall(request).execute()) {
      System.out.println(response.body().string());
    }
  }
}

Token Management Operations

Listing Your Tokens

View all active tokens for your account:

query UserTokens {
  userTokens {
    tokens {
      id
      readonly
      expiresAt
      label
      revoked
      createdAt
      lastUsedAt
      lastIp
    }
  }
}

Token Operations

Delete a specific token:

mutation DeleteUserToken($deleteUserTokenId: ID!) {
  deleteUserToken(id: $deleteUserTokenId)
}

Revoke a token (disable without deletion):

mutation RevokeUserTokens($revokeUserTokensId: ID!) {
  revokeUserTokens(id: $revokeUserTokensId)
}

Emergency: Revoke all tokens:

mutation RevokeAllTokens {
  revokeAllUserTokens {
    count
  }
}

Email Notifications

Security Monitoring

Enhance your account security by adding an email address for notifications:

mutation UpdateUserEmail($email: String!) {
  updateUserEmail(email: $email)
}

What you'll receive notifications for:

  • New IP address sign-ins
  • Token creation

Best Practices

💡 Most integrations never hit rate limits if batch mutations are used.

To achieve optimal performance and reliability when using the Nordomatic
GraphQL API, we recommend following these best practices.

Minimize Number of Requests

GraphQL is designed to reduce the number of network roundtrips.
Whenever possible, prefer fewer requests containing more data
instead of many small requests.

✅ Recommended:

  • Request multiple objects in a single query
  • Perform batch updates in one mutation
  • Select all required fields in one request

❌ Avoid:

  • Sending multiple sequential requests for related data
  • Updating objects one-by-one when they can be updated together

Example --- Preferred (Batch Writing Values):

mutation PerformWriteSignalValueByName($input: [WriteSignalValueByNameInput!]!) {
  performWriteSignalValueByName(input: $input) {
    ... on WriteSignalSuccess {
      id
      displayName
    }
    ... on WriteSignalError {
      name
      errorMessage
    }
  }
}

Variables --- Preferred (Batch Write):

{
  "input": [
    {
      "signalName": "AHU1.SupplyTemp.Setpoint",
      "value": "21.5"
    },
    {
      "signalName": "AHU1.Fan.Start",
      "value": "true"
    },
    {
      "signalName": "Zone3.Lighting.Level",
      "value": "75"
    }
  ]
}

Instead of:

performWriteSignalValueByName(signal A)
performWriteSignalValueByName(signal B)
performWriteSignalValueByName(signal C)

Reducing requests helps you:

  • Stay within rate limits
  • Reduce latency
  • Improve server performance
  • Increase overall system reliability

Request Only the Fields You Need

GraphQL allows precise data selection. Avoid requesting unnecessary
fields.

✅ Good:

query Values($signalNames: [String!]!) {
  signalValuesByName(signalNames: $signalNames) {
    values {
      name
      value
      statusCode
    }
  }
}

❌ Avoid requesting large objects if only small parts are needed.


Prefer Server Aggregation Over Client Loops

Avoid patterns where the client:

  1. Requests a list
  2. Loops through results
  3. Makes additional requests per item

Instead, structure queries so the server resolves relationships in one
request.


Handle Rate Limits Gracefully

The API enforces request rate limits per IP address.

Recommendations:

  • Batch operations when possible
  • Implement retry logic with backoff
  • Monitor rate limit headers
x-ratelimit-remaining
x-ratelimit-reset

Reuse Authentication Tokens

For applications and integrations:

  • Prefer API tokens over repeated login requests
  • Avoid authenticating before every request
  • Store tokens securely

Use Subscriptions for Real-Time Data

If you need continuous updates, prefer GraphQL subscriptions instead of
polling.

✅ Better:

  • One subscription connection
  • Server pushes updates

❌ Avoid:

  • Polling queries every few seconds

Subscriptions reduce server load and network traffic significantly.


Summary

Practice Benefit
Batch requests Lower latency & fewer rate limits
Request only needed fields Reduced payload size
Use subscriptions Efficient real-time updates
Reuse tokens Better security & performance
Prefer logical mutations Cleaner integrations

Support

Getting Help

Technical Support:


© 2025 Nordomatic. All rights reserved.