Platform

Connect your cloud application to any customer's on-premise Sage 100 Contractor, without opening inbound firewall ports.

Sage 100 Contractor runs on a server in your customer's office. Your application runs in the cloud. Everything hard about that gap is what Twinn is: the connector on their server, the connectivity between the two, and one REST API that reaches every customer the same way.

No inbound ports No database exposure No VPN required

The actual problem

Sage 100 Contractor has no cloud API for you to call

This is the part worth being precise about. Sage 100 Contractor is an on-premise product. There is no hosted endpoint you can authenticate against, and its data lives in a database on your customer's own server, in its own native format.

So a cloud product that needs Sage data has to solve two problems at once: read and write that database correctly, and reach it at all from outside the building. Twinn is both halves. The REST API is how you talk to it; the connector and the connectivity are what you are actually buying.

Sage 100 Contractor, not Sage 100 ERP. They are separate products on separate databases with different data models, so an integration built for one does not reach the other. Plenty of vendors connect to Sage 100 ERP. Far fewer connect to Contractor, and that is the one we built for.

The data is in the building

Sage 100 Contractor keeps its data in a database on your customer's server, in its own native format. There is no hosted endpoint to point a client at.

The perimeter says no

The usual ways in are an inbound firewall rule, a VPN, or exposing the database. Every one of those is a conversation with the customer's IT company that you will lose more often than you win.

And then there are hundreds of them

One customer is a weekend of work. A hundred customers, each with their own network, Sage version, and IT policy, is a product you now maintain instead of yours.

How a request reaches Sage

Your application calls one hostname over HTTPS. The request travels down a connection the customer's own server established outbound, and Sage answers back along the same path. Nothing in this diagram requires an inbound rule on the customer's firewall.

Your cloud application

Wherever it runs

HTTPS with mutual TLS, authenticated by a scoped API key

The OPDC host you were issued

One hostname per customer install

Passed down a connection the customer's server established outbound

OPDC connector

On the customer's Sage server

Local network, native database protocol

Sage 100 Contractor

The customer's existing database

No inbound ports·No database exposure·No VPN required

The customer's own hostname is the one detail worth noting early: each install is addressed individually rather than through a shared public endpoint, which is why the API reference cannot fire live requests at a demo server. The security page covers the deployment side for whoever has to approve it.

Getting started

From nothing to reading a customer's Sage data

The whole lifecycle, including the parts your customer does. There is no sales call in the middle of it and nothing here takes longer than a session.

  1. 1

    Create your Twinn account

    You

    You get the integrator dashboard: the connector download, key provisioning, and the logs and metrics for every call your customers make.

  2. 2

    Download the connector

    You

    One EV code-signed Windows executable from the download portal. The same build for every customer, so there is no per-customer artifact for you to keep track of.

  3. 3

    Provision an API key for the customer

    You

    Issued from the dashboard and scoped to that customer, so it can be revoked on its own without touching anyone else.

  4. 4

    Install the connector on the Sage server

    Customer

    Their IT company runs the installer and supplies two things: the API key you issued, and the database credentials for their Sage company. A customer running several Sage companies gets a key and credentials for each, all handled natively by the one install.

  5. 5

    The connector verifies it can work here

    Automatic

    On connecting to Sage it checks the version and the field compatibility. An unsupported version is refused rather than half-supported, so you find out at install time instead of in a write.

  6. 6

    Call the API

    You

    Your application addresses that customer on their issued host, using the key for the company you want. Nothing about the call differs from the next customer, which is the whole point.

Then step 6 looks like this

A read against that customer's install: POST /api/v3/parts/query. One POST, a documented envelope back, and the same shape on every one of the 82 readable entities.

Request

// POST https://{your-opdc-host}/api/v3/parts/query
// Authorization: your customer's API key
{
  "select": ["partCode", "partName", "partCost"],
  "filters": {
    "match": { "field": "partCost", "operator": ">=", "value": 10 }
  },
  "orderBy": ["partCost DESC"],
  "page": { "pageNumber": 1, "pageSize": 25 }
}

Response

{
  "asOf": "2026-08-09T09:14:02Z",
  "page": { "pageNumber": 1, "pageSize": 25, "totalCount": 142, "returnedCount": 25 },
  "data": [
    { "id": "3f9c...-uuid", "recordNumber": 4471,
      "partCode": "PIPE-001", "partName": "PVC Pipe 4\"", "partCost": 12.50 }
  ]
}

Every call, and every write, lands in your dashboard with its logs and metrics beside it.

Build or buy

Could you build this yourself? Yes. Here is the list.

Nothing about this is impossible. It is just a product of its own, sitting between you and the product you actually meant to build, and it needs maintaining for as long as you have customers.

Build it yourself

  • A Windows agent, packaged, signed, and installable by someone else's IT company
  • Reading and writing the Sage database correctly, in its native format
  • Every Sage business rule a write has to satisfy before it is allowed to land
  • A secure outbound connection, so no customer has to open a port
  • Certificate issuance, rotation, and renewal on every install
  • Authentication, key scoping, and per-customer isolation
  • Silent updates across every customer, and version compatibility as Sage moves
  • Retries, idempotency, and a way to answer "did that write actually commit"
  • Monitoring, logging, and tracing you can hand a customer during an incident
  • Front-line support for IT teams you have never met

Then keep all of it working across every Sage version and every customer's IT policy, forever.

Build on Twinn

  • Install the connector on the customer's Sage server
  • Authenticate with the API key issued for that customer
  • Call the REST API

Your team writes HTTP calls against a documented contract and gets on with the product.

Capabilities

What you can read and what you can write

Before you talk to sales, not after. This is the v3 contract as shipped: 82 entities you can read and 7 endpoints that write to Sage, across 12 areas.

Every write is a create, and each one runs the full cascade Sage would run itself: an AP invoice posts its journal, propagates account balances and writes job costs. Updates and deletes are not exposed in v3, deliberately. If your product needs one, tell us which and we will talk about it rather than leave you to find out from a 404.

Sage areaReadCreateWrite endpoints
Ledger8/ledger-transactions
Accounts Payable2/ap-invoices
Accounts Receivable2NoNone
Purchasing1/purchase-orders, /purchase-order-receipts
Inventory12/inventory-allocations
Jobs and Costing13NoNone
Project Documents5NoNone
Payroll12/payroll
Service Management7/service-inventories
Equipment4NoNone
Vendors and Clients10NoNone
Banking and Tax6NoNone

Read counts are entities, each with its own query contract. The API reference has the fields, the filters, and a request and response for every one.

Reliability

What happens when something is not up

A connector that reaches into someone else's building will meet a rebooting server, a locked Sage installation, and a database that is busy. The contract answers each of those with a specific status and a documented next move, so your retry logic is written against a rule rather than a guess.

504

The server did not answer

A documented outcome, not a mystery: the request was accepted but the on-premise service did not answer in time, so the result is unknown. Retry with the same idempotency key.

Idempotency-Key

Retries that cannot double-post

Stripe-style keys, scoped to your API key. The response is stored for 24 hours and replayed on a repeat, so a retry after a timeout returns the original outcome instead of posting twice.

dryRun

Test a write against live data

The whole cascade runs against the real Sage data inside one transaction and is then rolled back. You see exactly what would have happened without committing it.

423

Sage is locked for maintenance

You get a specific status naming the holder of the lock, with a Retry-After, instead of a generic failure while somebody runs a period close.

502

Sage itself refused

A constraint, a deadlock, a permission, or a dropped connection comes back as a database-side failure that is marked retryable, and honestly carries no Retry-After, because a deadlock clears in milliseconds and a revoked permission never does.

traceparent

One trace, end to end

Send W3C trace context and it is propagated to the on-premise service and into the request logs, so a slow call can be followed across the hop instead of guessing which side it was.

The service restarts itself

The connector runs as a Windows service and comes back after a reboot or a stop. Sage is unaffected either way, because the connector never modified it.

Failures name the field

A rejected write says which field failed and which rule it broke. Unknown fields are rejected rather than silently ignored, so a typo surfaces in development.

A Sage upgrade cannot break a write quietly

The connector checks the Sage version and the field compatibility when it connects. On a version it does not support it refuses reads and writes outright rather than guessing at a changed schema.

Updates are applied deliberately

Connector updates are installed rather than pushed silently, so nothing changes on a customer server without their IT company knowing. The version check is what stops an install drifting out of step unnoticed.

One install, every company

One install handles a multi-company Sage environment natively. Each company has its own key and credentials, and the key you call with selects the company, so no caller can cross from one to another.

The full taxonomy, including which codes carry a Retry-After and which deliberately do not, is on the developers page.

What the subscription covers

Not an API key. Production Sage connectivity: the on-premise connector, the secure connection between your cloud and every customer's server, read and write API access, multi-company support, updates, audit logging, and an engineer who already knows the Sage data model.

Integrators embedding OPDC in their own product add keys per customer once the Integrator license is in place, so the cost scales with your customer count rather than sitting as one flat number up front.