Skip to main content

API v1.1.0: archives, relationship properties, ownership, and scenarios

Integrations that synchronize a CMDB, a cloud inventory, or a project portfolio need the same capabilities as the application: archive and restore, qualify relationships, name who is accountable, and test changes before applying them. The API v1.1.0 brings these capabilities to /api/v1/, without changing the URL prefix your integrations already use, and adds create-or-update so a synchronization no longer has to look records up before writing them.

Archive, list, and restore​

DELETE /assets/{id} has always archived an asset rather than deleting it. Reads now let you see that archive. Every asset response carries archived_at, null while the asset is active. Pass archived=true to list archived assets only, or archived=any to read both, on the list and search endpoints. archived_at is filterable and sortable on the search endpoint, so a synchronization pulls the assets archived since its previous run.

Relationships follow the same logic with an archived flag and a new updated_at. GET /relationships?archived=true lists relationships archived on their own, whose two assets are still active; archived=any returns every relationship whatever its state, including those archived with one of their assets. Archiving updates updated_at, which the search endpoint now filters and sorts on.

Two new endpoints bring records back. POST /assets/{id}/restore reactivates an asset and the relationships it can, and returns their IDs. POST /relationships/{id}/restore reactivates a relationship and returns it as GET /relationships/{id} would. Both need an Editor key or higher and also work inside a scenario. Use them when a resource archived by a previous synchronization run reappears, instead of creating a duplicate.

Five bulk endpoints handle many records at once: POST /bulk/assets/archive, /restore, and /attest for up to 500 assets, POST /bulk/relationships/archive and /restore for up to 200 relationships. The response lists the IDs written in applied, and each identifier left untouched in skipped with a stable reason: not_found, forbidden, already_archived, already_active, or not_owner. The accepted records are written in one transaction. Other validation rules and plan limits still apply: a restore can fail on a uniqueness conflict or the asset limit.

Relationship properties and parallel relationships​

Relationship types can now carry their own properties, and the API reads and writes them. Relationship responses include a properties object limited to the values you are allowed to see. PATCH /relationships/{id} updates selected keys and leaves the others unchanged, and POST /relationships accepts properties at creation. The metamodel endpoints list each relationship type's properties, in the same form as an asset type's.

When a type has properties enabled, several relationships of the same type can link the same two assets. Pass allow_duplicate: true to create a new instance next to the existing ones, on POST /relationships or in an item of POST /bulk/relationships. The new instance must differ from the others by at least one property value, or the request fails with 409 identical_relationship_already_exists. Each instance has its own ID: keep it to update that relationship later. Asset responses with include=relationships list one entry per instance, and deleting a relationship archives that instance only.

Relationship types also accept the unique-key property type. Its value is unique among the active relationships of the type, and the API addresses a relationship by the composite key {relationship_type_key}:{unique_property_key}:{value} on GET, PATCH, DELETE, and POST .../restore for /relationships/{id}, and in relationship_id of PATCH /bulk/relationships. A synchronization that stores its own reference on each relationship no longer needs to keep Boldo IDs. An identifier that is neither a UUID nor a composite key fails with 400 invalid_identifier_format on these endpoints, as it already did for assets.

The new PATCH /bulk/relationships updates up to 10,000 relationships in one request. Like bulk creates and updates, it is all-or-nothing: every item is checked first, then all changes are written together. The response lists every relationship of the request in order, with a changed flag that tells whether the patch modified at least one property.

Create or update in one call​

The four create endpoints accept on_conflict. When the payload matches an existing record, update merges the sent fields into it and restores it first if it was archived, ignore leaves the record’s content unchanged, and the default error keeps today's 409. An asset is matched by the unique-key value in its properties; a relationship by a supplied unique-key value, falling back to the triplet type, source, target when no key matches.

Every response item now carries action: created, updated, restored, or unchanged, and the status is 201 only when everything was created. POST /bulk/assets and POST /bulk/relationships stay atomic. For assets, update and ignore require a unique-key value. For relationships, on_conflict: update or ignore cannot be combined with allow_duplicate: true. The asset endpoints also accept attest: true, with or without on_conflict, so a synchronization creates, updates, and confirms its records in one call, then spots what disappeared from its source with a last_attested_at filter. Repository quality must be enabled, and the key’s current owner must own every asset being attested. Attestation is unavailable inside a scenario.

Ownership, attestation, and completion​

Assets now carry owned_by_ids, last_attested_at, last_attested_by_id, and completion_score. Set owners on creation, update, and bulk requests with owned_by_ids; the users must be members of the organization. Owners are accountable for the asset, they receive no additional rights.

POST /assets/{id}/attest confirms that an asset is still correct without touching its content or updated_at. It is meant for synchronization systems that verify a record against its source of truth: the attestation is attributed to the key's current owner, who must be one of the asset's owners. Repository quality must be enabled on the organization.

Asset responses also expose stale_marked_at, stale_marked_by_id, and stale_mark_reason when someone requests a new attestation. Even a recently attested asset may need another review. The next attestation clears that request.

Filter and sort on these new system fields. filter[owned_by_ids]=<user_id> returns the assets a user owns. The search endpoints accept owned_by_ids, last_attested_at, last_attested_by_id, completion_score, and access_domain_keys as conditions, and last_attested_at and completion_score as sort fields.

Scenarios over the API​

GET /scenarios lists the scenarios visible to the key's user, paginated like the other lists, with their status, readiness, parent scenario, and number of changes. Add ?scenario={scenario_id} to a request to run it inside that scenario. Reads on assets, relationships, and the metamodel, including POST /search/* and POST /analytics/*, return the scenario's data. Asset and relationship writes are recorded in the scenario's changes, exactly like edits made in the application. Administrative endpoints refuse the parameter with 403 scenario_context_forbidden.

The scenario must be active, ready, and visible to the key's user, and scenarios must be included in the plan and enabled for the organization. Without the parameter, nothing changes: requests keep reading and writing the real data.

Impact analysis over the API​

POST /analytics/assets/impact runs the engine of the application's impact analyses without saving anything: sources, relationships to follow with their direction, maximum depth, stop conditions, and a display filter. Every asset reached comes back with its distance, the sources it stems from, the relationship types that reached it, and a structural criticality score, the number of other impacted assets that would be lost with it. The result is paginated, works inside a scenario, and returns detailed paths on request. A change-management tool can now ask Boldo what depends on a server before approving a change.

For a mandatory passage, such as "applications, then their risks, then the teams in charge, then everything those teams impact", send several stages: a plain analysis is a single one. Each stage follows its own relationships from the assets the previous stage reached, with an optional handoff_filter on what goes on to the next stage. Every impacted asset and path step carries its stage, and the summary tells why each stage stopped.

Rich text as Markdown​

Rich-text properties are now exchanged as Markdown, on reads and on writes. Headings, emphasis, links, lists, quotes, and code blocks round-trip; underline travels as <u>text</u>. Set rich_text_format=plain, as a query parameter or in a search body, to receive plain text instead. Length limits count the text without formatting, and contains filters match what a reader sees.

Computed properties and the typed specification​

Computed properties are read-only over the API. The metamodel endpoints flag them with computed: true and the typed specification marks them readOnly. Hidden properties no longer appear in the typed specification. The typed specification also gains archived_at and the scenario parameter, so generated clients can use these fields and parameters. It now types relationships like assets: each relationship type gets its own schemas, with its properties and the asset types of its source and target, on reads and creations.

Rotate an API key​

POST /api-keys/{id}/rotate replaces a key's secret and returns the new one. The key keeps its ID, name, type, roles, and owner; the previous secret stops working immediately, with no grace period. Any key can rotate itself, a Viewer key included, so an integration can renew its own secret on a schedule: the response is the only copy of the new value. Rotating another key needs an Editor or Administrator key, and only for a key it could create. A concurrent change to the key answers 409 api_key_changed.

Compatibility notes​

The specification version moves to 1.1.0. Three changes break integrations that read the fields concerned: review role fields, asset type relationships, and rich-text output, then the corrections below. The create responses gain an action field.

  • roles is removed from users and API keys. Read role_keys, an array of role keys.
  • relationships is removed from asset types, in GET /metamodel and the /metamodel/asset-types endpoints: each relationship type was repeated under its source and its target. Read relationship_types in GET /metamodel, where each type names its source and target once, or GET /metamodel/relationship-types?filter[asset_type_key]={key} for the relationship types of one asset type.
  • Rich-text values were returned as stored editor JSON. They are now Markdown by default. Integrations that parsed the JSON should read Markdown or request rich_text_format=plain.
  • properties.{key} in a search condition always designates a custom property, even when the key matches a system field such as name or created_at. Use the bare field name for system fields.
  • Deleting a relationship archives that relationship only. Parallel relationships of the same type between the same assets are left untouched.
  • POST /analytics/assets/aggregate returns exact figures for averages and distinct counts. meta.total is now the measure over all matched assets: it was the sum of the groups, which added up averages and counted an asset found in several groups more than once. With breakdown_by, a group's value is computed on the whole group instead of adding up its breakdown. data and each breakdown are sorted by value, largest first.
  • Two error codes now name their object, as the others do. A name already used by an asset of the type answers 409 asset_name_already_exists instead of name_already_exists, and an unknown asset type answers 404 asset_type_not_found instead of metamodel_not_found.
  • A key now grants at most its own type and roles, whatever its type, when it creates, updates, or rotates a key. An Administrator key that created keys with roles it does not hold receives 403 insufficient_privileges_roles: give it the roles it hands out. Resending a key's current type or roles is still accepted, and an Administrator key can still remove roles it does not hold.
  • No roles counts as the No role role: a key that holds roles can no longer empty a key's role_keys, and on POST /api-keys an empty or omitted role_keys gives the new key the calling key's roles instead of none.

Responses now carry exactly the fields the specification documents: a few undocumented internal fields, such as sourceAssetId on POST /relationships, are no longer returned. A 429 response gives the wait in retryAfterSeconds and in the Retry-After header.

Composite keys are stricter as well: when several assets share a unique-key value, the request fails with 409 ambiguous_asset_identifier instead of picking one. The 403 responses document their new codes, including scenario_context_forbidden, scenarios_disabled, and repository_quality_disabled.

Follow reading and synchronizing data and writing through the API to adapt your integration. The API reference details each capability, and the OpenAPI specification at /api/v1/openapi/base.json describes every endpoint.