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.
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
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
Create your Twinn account
YouYou get the integrator dashboard: the connector download, key provisioning, and the logs and metrics for every call your customers make.
- 2
Download the connector
YouOne 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
Provision an API key for the customer
YouIssued from the dashboard and scoped to that customer, so it can be revoked on its own without touching anyone else.
- 4
Install the connector on the Sage server
CustomerTheir 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
The connector verifies it can work here
AutomaticOn 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
Call the API
YouYour 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 area | Read | Create | Write endpoints |
|---|---|---|---|
| Ledger | 8 | /ledger-transactions | |
| Accounts Payable | 2 | /ap-invoices | |
| Accounts Receivable | 2 | No | None |
| Purchasing | 1 | /purchase-orders, /purchase-order-receipts | |
| Inventory | 12 | /inventory-allocations | |
| Jobs and Costing | 13 | No | None |
| Project Documents | 5 | No | None |
| Payroll | 12 | /payroll | |
| Service Management | 7 | /service-inventories | |
| Equipment | 4 | No | None |
| Vendors and Clients | 10 | No | None |
| Banking and Tax | 6 | No | None |
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.
504The 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-KeyRetries 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.
dryRunTest 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.
423Sage 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.
502Sage 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.
traceparentOne 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.