Skip to main content

Integrate your own system end to end

The end-to-end quickstart gets data into Dime.Scheduler. That is half an integration. This guide covers the whole loop: what to decide before you write anything, how to get the planning back out again, and what a custom integration needs before it can carry real work.

It is deliberately stack-agnostic. Everything here applies whether you use an SDK, the CLI, or raw HTTP, because they all wrap the same REST API.

The shape of an integration

Your integration is two independent channels, one in each direction, and they are built and configured separately.

Inbound, your system pushes master data and work into Dime.Scheduler: resources, jobs, tasks, and the filter and indicator catalogs that make them plannable. This is an API client you write and run.

Outbound, Dime.Scheduler pushes the resulting planning back to you. This is an HTTP endpoint you host and Dime.Scheduler calls.

The two fail independently, and the outbound half is the one most often left until last and then discovered to be the larger job. A planner scheduling work that never reaches your system is not an integration, it is a second system to keep in sync by hand. Plan for both from the start.

This is the same workflow the standard Business Central and NAV connectors follow. You are building what they provide.

Decide your source app and source type first

Before writing a line of code, settle these two values. They travel with every record, they cannot be changed casually afterwards, and almost every confusing failure downstream traces back to getting them wrong.

Source app identifies the system a record came from, for example ACME-ERP or CRM-EU. It does two jobs. It tells Dime.Scheduler which connector to call when a planner changes something, so the connector and the inbound data must agree on the same value. And it keeps records from separate systems distinct when they share a tenant: two unrelated SO100 job numbers stay separate because their source apps differ.

Source type identifies the kind of record within that system, for example SERVICEORDER or WORKORDER. It is what lets you route different kinds of work through different logic on the way back.

Two rules of thumb. Use one source app per system instance, not per system: a test ERP and a production ERP feeding the same tenant need different values. And keep the taxonomy small, because every source type you invent is another branch in your return-path code.

Model the data

Dime.Scheduler's model is small, and the relationships are what matter. A jobJobA high-level container, comparable to a project, that bundles related tasks together with common information such as customer, billing, and address. holds tasksTaskA unit of work that belongs to a job. It appears in the open task list until it is scheduled to a resource.; a task is what a planner schedules; scheduling one produces an appointmentAppointmentA task scheduled to a resource for a specific period - the scheduled instance you see on the planning board. against a resourceResourceAn entity that can carry out work - a person, vehicle, tool, or room - that you schedule on the planning board.. Filter groups and values drive who may see and plan what, and indicators drive what it looks like.

See architecture for the interactive model, and the data model reference for the full entity-relationship diagrams.

Map your entities onto that model before you start pushing. The common mistake is mapping one of your records onto a job when it should be a task, which only becomes obvious once planners find they cannot schedule at the granularity they need.

Build the inbound channel

Authenticate with an API key, which is the right choice here because a key represents a process rather than a person. Use OAuth instead only if you are building something user-facing where individual permissions or an audit trail of who did what matters.

Then push, in dependency order, because parents must exist before children:

  1. Master data: resources, filter groups and their values, categories and time markers.
  2. Transactional data: jobs, then the tasks beneath them.
  3. Assignments: filter values onto resources and tasks, indicators onto jobs.

The quickstart walks this with working code and an interactive curl walkthrough.

Two things worth designing in now rather than retrofitting:

Make it idempotent. The API upserts on the combination of source app, source type, and the record's own number, so re-sending the same record updates rather than duplicates. Lean on that: a sync you can safely re-run is one you can recover with, and the alternative is reconciling duplicates by hand.

Decide between push and poll. Pushing on change keeps Dime.Scheduler current but couples your write path to its availability. A periodic full or delta sync is simpler and more forgiving. Many integrations do both: push for responsiveness, and a nightly sync to repair anything the push missed.

Build the outbound channel

This is the half the quickstart does not cover, and the half that makes the loop real.

Use the webhook connector. It is the generic outbound path: every appointment change on the planning board fires a POST to a URL you own, carrying the full appointment as JSON. Register it in connectors against your source app, set the HTTP POST URI, and that is the entire Dime.Scheduler-side configuration.

Everything else is yours. The webhook connector deliberately does not interpret the payload, link it back to a record on your side, or run a workflow on success. Unlike the BC and NAV connectors it delivers the message and stops.

So your receiver needs to:

  • Authenticate the caller. Several types are supported. Prefer MS Entra ID or OAuth2 for anything crossing the public internet, since neither sends a credential on every call and both let you rotate secrets without redeploying. Use basic only if your receiver cannot speak OAuth2, and only over HTTPS. Use none only when network controls do the authentication for you.
  • Match the appointment back to your record. This is where source app and source type earn their keep, together with the job and task numbers you originally sent.
  • Handle create, update, and delete. Delete is the one that gets skipped, and it fails quietly: cancelled work stays live in your system and nobody notices for weeks.
  • Be idempotent. Assume a message can arrive twice.

If your receiver would mostly be glue, consider Power Automate instead. It registers its own connector behind the scenes and gives you hundreds of destinations without hosting anything.

Handle failures

Both channels fail, and they surface in different places.

Inbound calls return errors to your client directly, so handle them where you make the call.

Outbound failures land in Dime.Scheduler. The errors page lists every faulted appointment, with a Webhook tab that isolates yours from Dynamics and Exchange traffic. From there you can inspect an appointment's transactions and retry them, and webhook transaction errors explains the codes.

Two habits pay for themselves. Return an accurate status code from your endpoint rather than swallowing errors into a 200, because a receiver that always reports success makes the errors page useless and hides real failures. And decide who watches that page, since an errors list nobody owns silently accumulates until it is worthless.

Go to production

Build against the sandbox. Point the client at it, register the connector there, and keep a throwaway source app for experiments so test records never mix with real ones.

Before switching over, work through the whole loop deliberately:

  • Push master data and confirm resources appear on the board and tasks in the open tasks grid. Records that exist but never reach open tasks usually means the assignments were skipped or the source app and source type do not match between the task and its job.
  • Plan something and confirm it reaches your endpoint with a payload you can act on.
  • Move it, then delete it, and confirm both arrive.
  • Make your endpoint fail on purpose and confirm the failure shows up where you expect.
  • Re-run the inbound sync and confirm nothing duplicates.

Then create the production connector as a new connector rather than repointing the tested one, so the working configuration survives.

The Enabled flag on a connector is useful here: it lets you register production ahead of time without activating it, and gives you a way to silence a source during maintenance without deleting anything.

Where to go next

  • REST API reference for every endpoint.
  • SDKs for .NET, JavaScript, and Python.
  • CLI for scripting and CI.
  • If you would rather not build and maintain a connector at all, get in touch about making yours a standard one.