NAVIGATION
ELOWEN DOCUMENTATION

Last updated: 10 October 2026

Browse documentation · raynet implementation
Developer reference

raynet implementation

Credential settings

The Raynet page is the only editor for personal credentials, declared with userConfigPlacement: "pluginPage". Company credentials, instance name and the personal-login policy remain in the administrator's plugin settings. The page uses the host's SettingsDocument, SettingsGroup, SettingsRow and Toggle. Its credential group starts closed and preserves the browser's remembered choice under raynet.page.credentials. Opening it or typing does not save.

The personal form reads the masked snapshot and revision from GET /plugins/user-config, then submits changed values through PATCH /plugins/raynet/user-config with expectedRevision. It never names an account or uses a separate credential store. Empty keys preserve the stored key; switching to the company account keeps the saved personal username and key. An accepted replacement is removed from the form immediately, including when the following connection test fails.

Save confirmation and connection testing are separate results. Saving explicitly runs the existing GET /plugins/raynet/api/status test; "Test saved credentials" can repeat the test without another write. The status route reports failure: "rejected" for the client's HTTP 401 and failure: "failed" for other test failures. The UI does not infer either from error prose. A failed status or configuration load offers retry instead of guessing the personal-login policy.

A revision conflict retains the draft. Reload discards it and reads the stored snapshot; keep edits adopts the latest revision while preserving changed local fields, then requires another explicit save. Unchanged local fields retain concurrent remote changes. An interrupted write response is unconfirmed and requires reload before another write.

Personal credentials are used only when allowed and explicitly selected. An incomplete selected personal login is an error, never a fallback to the company account. When personal credentials are disallowed, stored personal values remain intact but are ignored. A run without an account uses company credentials when configured. A run without an account and without company credentials has no Raynet identity and fails with an explicit message.

Owning code: web-src/ConnectPanel.tsx (the form), lib/client.mjs (resolveCredentials, the credential reasons), lib/routes.mjs (the status route), and src/api/routes/plugins/userConfig.ts in core, which stores the values and checks the revision.

The Raynet browser runtime consumes UI API 55. apiJson accepts the published PluginApiRequestInit, and apiPatch forwards { method: 'PATCH', json: payload } to the authenticated host API. The host owns serialization and Content-Type; requests without json retain their raw RequestInit bodies. ConnectPanel uses this path to submit the masked credential revision and changed values. The runtime also exposes the published interpolate type for ConnectPanel and RaynetWorkspace: all known placeholders receive literal string or number values, including dollar sequences, and unknown placeholders remain unchanged. Neither helper has a local fallback; the page requires the host runtime and the manifest and registration both require API 55.

Browser route scope and request previews

The read-only search and record routes use the registry's bindPath, just like the tools. Search accepts operations needing no path arguments; detail supplies only a positive numeric id. A sub-resource needing parent_id or an entity addressed by name returns HTTP 400 before credentials are resolved or a request is sent. A missing credential returns HTTP 428 with a reason code. The browser catalog offers only top-level listable entities. Use the tools with parent_id for supported sub-resources, or RaynetRequest for named paths. The registry remains the authority for path casing and trailing slashes.

The CRM client owns GET query normalization. Every request through client.request, including /security/info, defaults to dateFormat=ISO8601; dry-run URLs use the same normalizeQuery helper. An explicit dateFormat is preserved, including an empty string. Null and undefined query values remain absent from the URL. PUT, POST and DELETE receive no date-format default. For example, the page's status route calls request with method: 'GET' and no query. The existing shared HTTP transport still owns query serialization, authentication headers, cancellation and retries.

All five dry_run tool previews apply these defaults without sending a request. Raw writes and their previews still require raw requests to be enabled, full access mode and confirm: true.

Meeting automation control

The plugin publishes raynetMeetings through the existing plugin control seam. It is the seam for a plugin that automates meetings and needs CRM reads and one tag write, without holding its own Raynet client or credentials.

Minimal use, in a consumer plugin:

const raynet = ctx.control('raynetMeetings');
if (!raynet) return reportUnavailable();
const meetings = await raynet.list({ from: '2026-10-09', to: '2026-10-10' });

A consumer such as meeting-confirm declares consumesControls and requiresControls: ["raynetMeetings"], grants capabilities.reads: ["controls"], and resolves ctx.control('raynetMeetings') at call time. If Raynet is disabled or unavailable the control is absent; the consumer must report unavailable rather than treating this as an empty CRM.

MethodContract
list({ from, to, signal? })ISO dates or ISO date-times; returns an array of raw meeting records with scheduledFrom >= from and scheduledFrom < to, across all pages.
get(id)Positive numeric meeting id; returns the raw meeting record.
person(id)Positive numeric contact-person id; returns the raw person record.
businessCase(id)Positive numeric business case (deal) id, as in a meeting's businessCase.id; returns the raw business case record, whose description holds the deal note.
update(id, { tags, description })Both fields are strings. POSTs exactly these two fields to the existing meeting update operation; resolves without a value.

All methods reuse the tools' prepare and send: credentials and access mode are resolved on each call, unattended calls use the configured company login, and update requires read_write or full. The existing HTTP timeout, concurrency limit, read retry and error mapping also apply. Errors reject, including malformed API envelopes. Reads request dateFormat=ISO8601; the existing date-filter normalizer supplies Raynet's accepted wire spelling. List requests use the existing page size and stable id ordering. Each page costs one API request; records are returned without display projection. The consumer filters trigger tags and decides which statuses are eligible.

Real consumer: meeting-confirm, in the plugin registry, which lists tagged meetings for the day and reads each deal note through this control.

Field evidence and release verification

The committed generated registry describes request fields and filters, not response schemas. The existing request fixtures and schema establish the following names, but do not prove every nested response shape:

EntityCanonical names supported by the repository
Meetingid, title, scheduledFrom, scheduledTill, status, completed, description, tags, owner, person. The date-filter fixture is in tests/raynetPlugin.test.ts in the plugin registry repository; the remaining request fields are in lib/registry.generated.mjs.
Personid, firstName, lastName, contactInfo. The registry lists contactInfo.email and contactInfo.email2 as filter names and contactInfo as an object.

There is no captured meeting/person response fixture proving owner email or telephone field names. In particular, owner.email, scalar versus object owner/person references, phone keys inside contactInfo, mobile preference, status values, and read-side tag shape remain unverified consumer assumptions. Do not add guessed alternate fields. The raw control intentionally does not normalize them; the meeting-confirm consumer must document and validate its one chosen schema.

The generated update schema declares tags: string, whereas create declares tags: string[]. This control accepts the update type and does not join arrays or guess a delimiter. A comma-separated tag string is an unverified write-format assumption. Verify owner routing, phone fields, status and tag response shape, and a tag/description write against tagged pilot records before release. All implementation checks use mocked APIs; no live field verification was performed.

Registry generation

lib/registry.generated.mjs is generated from Raynet's OpenAPI document by tools/generate-registry.mjs, run by hand when Raynet publishes a new API version. The generated file is the only authority on methods and paths. Do not edit it by hand.

Owning code

  • index.mjs: the settings normaliser, the tools, the raynetMeetings control and the skill registration.
  • lib/client.mjs: the HTTP client, credential resolution, error mapping and the rate-limit note.
  • lib/routes.mjs: the browser routes under /plugins/raynet/api/.
  • lib/registry.mjs and lib/registry.generated.mjs: the operation table, the access authority of each operation, and path binding.
  • lib/params.mjs and lib/format.mjs: argument checks and the text the tools return.
  • web-src/: the browser page (RaynetWorkspace.tsx, ConnectPanel.tsx, runtime.ts).
  • skills/raynet-crm/SKILL.md: the playbook the assistant loads before working in Raynet.
  • elowen-plugin.json and i18n/: the manifest, the labels, and the Czech and Slovak overrides.
  • tests/meetings.test.mjs in the plugin, and tests/raynetPlugin.test.ts in the plugin registry repository.

When the plugin is disabled or not granted to a person, its tools, its page and its control are absent. A scheduled job or webhook that acts for nobody has no personal login and uses the company account only.