View Categories

Integration Contract

9 min read

The Integration Contract defines the minimum technical and operational rules that every INTERA Integration Module must follow.

An Integration Module is not just a script that connects to an external system. It is a reusable package that INTERA Core can discover, configure, execute, monitor, and eventually publish through the INTERA Online Store.

A valid Integration Module should clearly describe:

- what the integration is;
- how it connects to the external system;
- what configuration is required;
- what DataSources it exposes;
- whether it supports entity synchronization;
- how errors are reported;
- how versions and compatibility are handled.

The contract exists so that all integrations behave consistently, regardless of whether they connect to an API, database, file export, legacy platform, or third-party SaaS product.

Required metadata #

Every Integration Module must declare basic metadata.

This metadata allows INTERA to identify the module, display it to administrators, validate compatibility, and understand which capabilities the integration provides.

A minimal metadata definition may look like this:

manifest: {
  id: "example_billing",
  name: "Example Billing",
  version: "1.0.0",
  kind: "direct_api",
  capabilities: ["datasource"],
  defaultCacheTtlSec: 300
}

Required metadata fields:

id
Unique technical identifier for the integration.

name
Human-readable integration name.

version
Current version of the Integration Module.

kind
General integration type, such as API, SQL, file, or legacy integration.

capabilities
List of features supported by the integration.

defaultCacheTtlSec
Default cache duration for DataSource results, where applicable.

The integration id should be stable. It should not change between versions unless the integration is being replaced by a different module.

Good integration IDs:

odoo
example_billing
enigma_billing
fleet_manager
ship_certificates

Poor integration IDs:

test
new_plugin
client1
temporary
billing_v2_final

The name should be readable by an INTERA administrator. It may contain spaces and product names.

The version should be updated whenever the integration package changes. Versioning is especially important once an integration is used by multiple customers or published through the Online Store.

The kind helps INTERA and developers understand the technical nature of the integration. Example kinds may include:

direct_api
readonly_api
sql
file
legacy

The capabilities field declares what the integration can provide. Example capabilities may include:

datasource
entity_provider
sync
action

For a first integration, the usual capability is:

datasource

Authentication #

Authentication defines how the Integration Instance connects securely to the external system.

The Integration Module should describe which credentials or access parameters are required. The actual credential values belong to the Integration Instance configuration, not to the reusable module itself.

Common authentication methods include:

- API key;
- bearer token;
- username and password;
- OAuth access token;
- client ID and client secret;
- database username and password;
- SFTP username and password;
- SSH private key;
- certificate-based authentication.

Sensitive values must be treated as secrets. These may include:

- passwords;
- API keys;
- bearer tokens;
- refresh tokens;
- client secrets;
- private keys;
- database credentials;
- SFTP credentials.

A configuration schema should clearly mark secret fields.

Example:

configSchema: [
  {
    key: "baseUrl",
    label: "API Base URL",
    type: "string",
    required: true
  },
  {
    key: "apiKey",
    label: "API Key",
    type: "secret",
    required: true
  }
]

An Integration Module should never hard-code customer credentials.

Do not do this:

const apiKey = "customer-production-api-key";

Use runtime configuration instead:

const apiKey = context.config.apiKey;

Authentication errors should be reported clearly. For example:

Cannot authenticate with Example Billing API. The API key was rejected by the external system.

Avoid generic messages such as:

Connection failed.

When possible, integrations should include a lightweight connection test. This allows INTERA administrators to confirm that credentials are valid before enabling DataSources or synchronization.

Initialization parameters #

Initialization parameters are the configuration values required to create and use an Integration Instance.

They describe how INTERA should connect to a specific external environment.

For example, one Integration Module may support many Integration Instances:

Example Billing - Production
Example Billing - Test
Example Billing - Cyprus Company
Example Billing - UK Company

Each instance may have different initialization parameters.

Common initialization parameters include:

- API base URL;
- tenant ID;
- company ID;
- environment name;
- default currency;
- timezone;
- database host;
- database name;
- file path;
- SFTP server;
- export folder;
- external system version.

A basic initialization schema may look like this:

configSchema: [
  {
    key: "baseUrl",
    label: "API Base URL",
    type: "string",
    required: true
  },
  {
    key: "tenantId",
    label: "Tenant ID",
    type: "string",
    required: false
  },
  {
    key: "timezone",
    label: "Default Timezone",
    type: "string",
    required: false
  }
]

Initialization parameters should be understandable to the person configuring the integration. In many cases, this will be an INTERA administrator, not the developer who wrote the module.

Good parameter names:

API Base URL
Tenant ID
Company Code
Default Timezone
SFTP Folder
Read-only Database Name

Poor parameter names:

x_mode
magic_id
legacy_flag
param2
custom_internal_toggle

Initialization parameters should be stable. Avoid changing parameter keys between versions unless there is a clear migration path.

Sync parameters #

Sync parameters control how and when INTERA retrieves data from the external system.

Not every integration requires synchronization. Some integrations only expose on-demand DataSources. However, integrations that import business entities or refresh stored data should define their sync behavior clearly.

Sync parameters may include:

- sync interval;
- enabled or disabled state;
- entity types to sync;
- default date range;
- maximum records per sync;
- pagination size;
- retry policy;
- timeout;
- timezone;
- full sync vs incremental sync;
- manual sync support.

A simple sync configuration may look like this:

syncConfig: {
  enabled: true,
  mode: "full_snapshot",
  intervalSec: 3600,
  timeoutSec: 60
}

An Entity Provider may define the type of entity it synchronizes and the stable external key used to identify each record.

Example:

entityProviders: [
  {
    entityType: "customer",
    keyField: "id",
    handlerName: "syncCustomers",
    schedule: {
      kind: "interval",
      everyMs: 3600000
    },
    handler: async (context) => {
      return [
        {
          id: "CUST-1001",
          name: "Example Customer Ltd",
          status: "active"
        }
      ];
    }
  }
]

Good sync behavior should be repeatable. Running the same sync twice should not create duplicate records.

Each synchronized entity should include a stable key. Without a stable key, INTERA cannot reliably match external records to local entities or Assets.

Recommended sync rules:

- return one clear entity type per Entity Provider;
- use stable external identifiers;
- return predictable field names;
- support pagination where needed;
- handle empty results safely;
- treat missing or inactive records consistently;
- report partial failures clearly;
- avoid uncontrolled full-system dumps;
- avoid deleting local records unless explicitly supported.

For most business systems, it is safer to mark missing records as inactive rather than immediately delete them from INTERA.

DataSource definitions #

DataSource definitions describe the datasets that the Integration Module can provide on demand.

Each DataSource should have a stable technical ID, a human-readable name, and a handler function that returns structured data.

A basic DataSource definition may look like this:

dataSources: [
  {
    id: "invoice_summary",
    name: "Invoice Summary",

    getData: async (params, context) => {
      return {
        generatedAt: new Date().toISOString(),
        totalOpen: 42,
        totalOverdue: 7,
        currency: "EUR"
      };
    }
  }
]

A DataSource should be designed around a clear business use case.

Good DataSource IDs:

customers
invoices
overdue_invoices
payment_summary
active_services
open_tickets
vessel_positions
certificate_expiry

Poor DataSource IDs:

query1
data
dump
misc
temp_report
raw_everything

A DataSource may accept parameters.

Common parameters include:

from
to
limit
status
customerId
assetId
currency
productCode
period
timezone

Example:

getData: async (params, context) => {
  const from = params.from;
  const to = params.to;
  const status = params.status || "open";

  return {
    paramsUsed: { from, to, status },
    items: []
  };
}

DataSource parameters must be validated before they are used in external API calls, SQL queries, file paths, or other sensitive operations.

A good DataSource response should be:

- structured;
- predictable;
- documented;
- reasonably sized;
- safe to cache where appropriate;
- clear about the time period used;
- clear about when the data was generated.

Recommended response fields include:

generatedAt
period
items
count
summary
source
warnings

Example:

{
  "generatedAt": "2026-07-17T10:30:00Z",
  "period": {
    "from": "2026-07-01",
    "to": "2026-07-31"
  },
  "count": 2,
  "items": [
    {
      "customerId": "CUST-1001",
      "openInvoices": 3,
      "overdueAmount": 1200,
      "currency": "EUR"
    },
    {
      "customerId": "CUST-1002",
      "openInvoices": 1,
      "overdueAmount": 350,
      "currency": "EUR"
    }
  ]
}

DataSources should not expose unnecessary internal structures from the external system. They should return useful business data in a clean format that INTERA dashboards, Metrics, and role packages can consume.

Error handling #

Every Integration Module must handle errors clearly and predictably.

External systems can fail for many reasons. APIs may reject credentials. Databases may be unavailable. Files may be missing. Data may be malformed. Rate limits may be reached. Network requests may time out.

Common error types include:

- invalid configuration;
- missing required credentials;
- authentication failure;
- authorization failure;
- unreachable external system;
- timeout;
- rate limit;
- malformed external response;
- missing file;
- invalid file format;
- database connection failure;
- SQL query error;
- unsupported parameter;
- empty result;
- partial sync failure;
- external system maintenance.

An integration should convert technical failures into clear operational messages.

Useful error message:

Cannot load invoices from Example Billing. The external API returned 401 Unauthorized. Check the API key for this Integration Instance.

Poor error message:

Fetch failed.

Errors should include enough context to help an administrator or developer understand the problem, but they must not expose secrets.

Do not include sensitive values in error messages:

- API keys;
- passwords;
- tokens;
- private keys;
- database passwords;
- full connection strings containing credentials.

For DataSources, the error should make it clear whether the problem is temporary or configuration-related.

Examples:

Temporary:
The external API timed out after 30 seconds.

Configuration-related:
The API Base URL is missing.

Authentication-related:
The API key was rejected by the external system.

Data-related:
The CSV file does not contain the required column: customer_id.

For synchronization, error handling should also explain whether the sync failed completely or partially.

Example:

Customer sync completed with warnings. 1,238 records were imported, 14 records were skipped because they did not contain a stable customer ID.

Good error handling makes integrations easier to operate and support. It also helps INTERA show meaningful status indicators, such as valid, delayed, missing, warning, or error.

Versioning #

Versioning allows INTERA, developers, and customers to understand which version of an Integration Module is installed and whether it is compatible with the current environment.

Every Integration Module should declare a version in its metadata.

Example:

manifest: {
  id: "example_billing",
  name: "Example Billing",
  version: "1.2.0",
  kind: "direct_api",
  capabilities: ["datasource", "entity_provider"]
}

Recommended version format:

MAJOR.MINOR.PATCH

Use version changes consistently:

PATCH
Bug fixes that do not change the public integration contract.

MINOR
New DataSources, optional parameters, new entity providers, or backward-compatible improvements.

MAJOR
Breaking changes, renamed DataSources, removed fields, changed configuration keys, or changed response formats.

Examples:

1.0.1
Fixes timeout handling for invoice requests.

1.1.0
Adds payment_summary DataSource.

2.0.0
Renames customer_balance DataSource and changes required configuration fields.

Avoid breaking changes where possible. Third-party integrations may be used by dashboards, Metrics, role packages, and customer-specific configurations. Renaming a DataSource, changing a field name, or removing a parameter can break existing INTERA setups.

Breaking changes should be clearly documented and should include migration notes.

A version release note should explain:

- what changed;
- whether the change is backward-compatible;
- whether configuration changes are required;
- whether dashboard or Metric mappings may be affected;
- whether users need to re-run synchronization.

For future Online Store publication, versioning will also support review, compatibility checks, upgrades, rollback, and customer notifications.

Until the Online Store is available, developers should still keep clear version numbers and changelogs for every Integration Module.