Evaluation
Technical overview
The Partner API is a server-to-server REST API over the records your customers already keep in Visena — people, companies, projects, activities and documents. One OAuth 2.0 credential, nine resources under one versioned contract, and writes that run through the same application services as Visena's own screens. This page is the architecture and the security model, for the people who have to sign the integration off.
One integration, the real Visena engine
Your systems create, read, update and delete the records that matter — people and companies, projects, activities, documents — inside a customer's Visena instance. There is no separate integration database and no nightly copy: a write lands on the same record a user would have edited, through the same application services, so validation and business rules behave as they do in the interface.
Authoritative data
You read and write the record itself, live. Not an export, not a replica that has to be reconciled afterwards.
Secure by design
OAuth 2.0 client credentials, opaque bearer tokens, and a credential bound to one instance. Nothing is bolted on afterwards.
Predictable and standard
REST over HTTPS with JSON bodies, RFC 7396 merge-patch, RFC 9457 problem documents, RFC 8288 navigation links. What your team already knows applies.
The practical consequence is that your integration inherits the platform's correctness rather than reimplementing it. A rule the interface enforces — a required field, a duplicate organisation number, a reference that must resolve — is enforced on your call too, and the failure comes back as a machine-readable problem document instead of a half-written record.
What the API gives you
A CRUD and synchronisation surface over the core CRM and work records, with the conventions an integrator expects. Everything below is available on the resources documented in the reference; the per-resource pages are the contract.
| What you get | How it works |
|---|---|
| Create, read, update, delete | Per-verb request bodies and a full-entity response. PUT replaces, and PATCH with application/merge-patch+json (RFC 7396) changes exactly the fields you send — set one, clear one with null, leave the rest untouched. |
| Three read modes on a collection | Offset paging with a real total for a screen, keyset (cursor) paging for a one-off extract, and a time-windowed list for “what changed since”. Documents add their own change feed at document/changes. |
| Masked identifiers | Records are addressed by per-instance masked ids. The raw database key never appears on the wire, in either direction; you carry the masked id verbatim between systems. |
| Structured errors | RFC 9457 application/problem+json, with the categories kept apart: 400 for a body that failed validation, 422 for a reference that does not resolve. Failures are programmatically actionable. |
| Your own keys | externalIds carry your own system's keys on a person, and the person list filters on an exact external reference — so you reconcile against your own records without adopting Visena's ids as your primary key. |
| Server-to-server only | No browser, no cookies, no session — and therefore no CSRF surface. The API is designed for backends, schedulers and ETL jobs. |
| The actor is named | Where a call is made on behalf of a named user, the response names that actor as actedBy beside the subject of the change — two identities on one record, not an anonymous machine event. |
| Attribution on every write | A write is attributed to a real person in the instance: the record carries created/createdBy and modified/modifiedBy, exactly as a change made on screen does. |
The three read modes, side by side
They are three different jobs, and picking the wrong one is the most common integration mistake we see. The list endpoints and their guarantees are covered in full under Lists, paging and sync.
# a page with a real total — for a screen or a picker
GET /acme/api/v1/en/person?offset=0&limit=50
# keyset paging — for a one-off extract, walked start to finish
GET /acme/api/v1/en/person/cursor?limit=100
# a time window — for ongoing synchronisation
GET /acme/api/v1/en/person/timeline?key=modified&since=2026-08-20T02:00:00Z
Two properties to design around. The time-windowed list is at-least-once: it may hand you the same record twice at a window boundary, so de-duplicate by id. And no list mode reports deletions — a record that is gone simply stops appearing, so reconciling disappearances stays on your side. Both are stated per endpoint in the reference.
A surface that keeps growing
Nine resources are live today, all under one contract, one credential and one authentication scheme. The path carries an explicit version, so a resource added later is a new path under the same v1 — widening what your integration covers is new code against a familiar surface, not a re-integration.
| Resource | Path | What it holds |
|---|---|---|
| Person | /person | Contacts, employees and user accounts — names, roles, addresses, e-mail aliases, your own external ids. Includes an advisory duplicate probe and a planned-deletion date. |
| Company | /company | Organisations — identifiers, addresses, status and relations, with organisation-number validation and its own duplicate probe. |
| Company template | /company-template | The shapes new companies are created from, as the instance is configured. |
| Project | /project | The engagements that work, time and billing are organised around. |
| Project template | /project-template | The shapes new projects are created from — read them to drive template-based creates. |
| Activity | /activity | Tasks and follow-ups, filterable by project, company, responsible, status or due date. |
| Document | /document | Real files stored against a person, a company or a project — upload, replace, download. |
| Document folder | /document/folder | The folder structure documents are filed in, including whole-tree download. |
| Document changes | /document/changes | A change feed over the archive: what was added or replaced since your last run. |
Resource names are singular. /person, not /persons — and there is no partner/ segment in the path. An older plural form appears in some early material; the current shape is the one in the reference, and it is what the service answers.
Architecture at a glance
You talk to one public edge. It authenticates the call, resolves it to exactly one instance, and routes it to the engine. Everything behind that edge is internal to Visena and never something your integration addresses.
- Authenticate. Exchange your client id and secret for an access token at
/{instanceName}/auth/api/v1/token, using the OAuth 2.0 client-credentials grant. - Call. Send the token as an ordinary
Authorization: Bearerheader to any data endpoint. There is nothing else to sign. - The edge validates and isolates. The token is checked, and the instance named in the path has to be one the token is granted for. A call outside that grant is rejected at the edge, before it reaches any data.
- The engine applies the same logic as the product. Writes go through Visena's own application services, so rules, validation and downstream effects fire as they would for a change made on screen.
Anatomy of a URL
Every call has the same shape: the instance, the API version, a locale, then the resource.
en or no
/person the resource, always singular
The locale decides the language of the messages that come back, not which records you see. The instance segment selects the customer, and it has to be one your token is granted for — a mismatch is refused at the edge rather than answered with someone else's data.
What your security team will want to know
The controls below are properties of the platform rather than options an integrator has to switch on. Credentials and tokens has the operational detail, and Read-only access walks through a credential that demonstrably cannot write.
- Opaque tokens. An access token is a reference value, not a container: there is nothing in it to decode, and it expires on its own — your backend renews it. A leaked token is a bounded window rather than a readable copy of anything.
- Secrets shown once. A credential secret is displayed a single time, at creation, and stored only as a one-way hash — it cannot be read back, by you or by Visena. A lost secret is replaced, never recovered.
- Per-instance isolation. A credential is granted to one instance, and the instance in the path has to match that grant — the edge resolves it, checks it, and refuses a mismatch. Behind that, each instance has its own database, so a request bound to one customer has no path to another's rows.
- Explicit scopes, and no permissive default. A token request has to name the scopes it wants: one that names none is refused with
invalid_scope, not quietly granted everything. The read/write split is then enforced per request, in a filter ahead of the endpoint — which is why a credential issued for reading cannot write at all, rather than merely being expected not to. - Masked identifiers. Records are addressed by masked ids, and the mask is derived per instance: the raw database key never crosses the wire in either direction, and the same record does not carry the same id in two different instances.
- Revocation. Revoke a credential and the tokens it minted stop working — expect the change to take effect within about a minute rather than instantly, because validation results are cached briefly. Offboarding a vendor is one call, not a wait for expiry.
- Attribution, never anonymous machine activity. A write through the API is attributed to a real person in the instance, in the same
createdByandmodifiedByfields the interface fills in — and where a call was made for someone else,actedBynames the actor as well. There is no unattributed API change.
The customer holds the keys, not the partner. Credentials are created by an administrator inside the customer's own Visena instance, scoped there, and revoked there. Visena does not issue a partner a standing key to a customer's data, and a customer can end an integration without involving us.
How your team integrates
Three steps, and the first call is usually the same afternoon.
- Get a credential. An administrator in the customer's Visena instance creates a partner credential for your integration and passes you the client id and secret securely. See Credentials and tokens.
- Obtain a token. Your backend exchanges the credential for an access token at the token endpoint and refreshes it before it expires — a few lines in any HTTP stack. Getting started has it working end to end.
- Build against the reference. Making requests covers headers, verbs and merge-patch, Errors and troubleshooting covers the failure modes, and the reference documents every endpoint, field and status per resource.
When you are ready for production, Going live is the checklist: scoped credentials, a rehearsed rotation, and what to monitor.
A foundation you can build a product on
The Partner API gives your team a standards-based path to data your customers already trust to Visena, with the platform's security model, instance isolation and business logic behind every call. There is no second data model to learn and no integrity gap between what your integration does and what the product does: the same services, the same rules, the same attribution.
Nine resources are live under one contract today, and the surface has grown by adding resources to it rather than by changing how you connect. That is the property worth designing against — the credential, the token exchange, the path shape and the error format you integrate once are the ones you keep.