Archi Model API Server — Execution Guide
This skill enables agents to execute real modeling operations in Archi by running CURL commands against the Archi Model API Server (http://localhost:8765).
Prerequisites
Before any modeling operation, verify the server is running:
curl -s http://localhost:8765/health
Expected: JSON with "status": "healthy". If this fails, the server is not running — tell the user to start it from Archi's Scripts menu.
Core Concepts
Sync vs Async Endpoints
| Type | Endpoints | Behavior |
|---|---|---|
| Sync | /health, /model/query, /model/search, /model/element/{id}, /views, /views/{id}, /folders | Return result immediately |
| Async | /model/apply | Returns operationId; must poll /ops/status?opId=... for result |
The tempId System
When creating elements/relationships, assign a tempId string. After the operation completes, the result maps each tempId to the real Archi element ID. Use real IDs for all subsequent operations.
Within a single /model/apply batch, later operations can reference earlier tempId values directly (e.g., a createRelationship can reference a tempId from a createElement in the same batch).
Visual IDs vs Concept IDs
- •Concept ID: The ArchiMate element/relationship ID in the model (returned by
createElement/createRelationship) - •Visual ID: The diagram object ID on a specific view (returned by
addToView) - •
addConnectionToViewrequires visual IDs forsourceVisualIdandtargetVisualId, NOT concept IDs
The Async Workflow Pattern
Every mutation follows this pattern:
1. POST /model/apply → { "operationId": "op-123" }
2. GET /ops/status?opId=op-123 → { "status": "queued" }
3. GET /ops/status?opId=op-123 → { "status": "processing" }
4. GET /ops/status?opId=op-123 → { "status": "complete", "result": [...] }
Polling implementation:
# Submit changes
OP_ID=$(curl -s -X POST http://localhost:8765/model/apply \
-H "Content-Type: application/json" \
-d '{"changes": [...]}' | jq -r '.operationId')
# Poll until complete (wait 1 second between attempts)
while true; do
STATUS=$(curl -s "http://localhost:8765/ops/status?opId=$OP_ID")
STATE=$(echo "$STATUS" | jq -r '.status')
if [ "$STATE" = "complete" ] || [ "$STATE" = "error" ]; then
echo "$STATUS"
break
fi
sleep 1
done
In practice: Run the POST, then run the GET poll. Parse the JSON result to extract real IDs. Most operations complete within 1-2 seconds.
API Quick Reference
Read Operations (Sync)
| Endpoint | Method | Purpose |
|---|---|---|
/health | GET | Server health check |
/model/query | POST | Model summary + sample elements |
/model/search | POST | Search by type, name pattern, properties |
/model/element/{id} | GET | Single element with relationships & views |
/views | GET | List all views |
/views/{id} | GET | View detail with elements + connections |
/views/{id}/validate | GET | Validate view connection integrity |
/folders | GET | Full folder hierarchy |
Write Operations
| Endpoint | Method | Type | Purpose |
|---|---|---|---|
/model/apply | POST | Async | Create/update/delete elements, relationships, views |
/model/plan | POST | Sync | Dry-run validation (no mutation) |
/views | POST | Sync | Create a new empty view |
/views/{id}/layout | POST | Sync | Auto-layout with Dagre |
/views/{id}/export | POST | Sync | Export as PNG/JPEG |
/views/{id}/router | PUT | Sync | Set connection router style |
/model/save | POST | Sync | Save model to disk |
/shutdown | POST | Sync | Stop the server |
All /model/apply Operation Types
| Operation | Required Fields | Optional Fields |
|---|---|---|
createElement | type, name | tempId, documentation, properties, folderId |
createRelationship | type, sourceId, targetId | tempId, name, documentation, properties, accessType |
updateElement | id | name, documentation, properties |
updateRelationship | id | name, documentation, properties |
deleteElement | id | cascade (default true) |
deleteRelationship | id | |
setProperty | id, key, value | |
moveToFolder | id, folderId | |
createFolder | name, parentFolderId | tempId |
addToView | viewId, elementId | tempId, x, y, width, height |
addConnectionToView | viewId, relationshipId, sourceVisualId, targetVisualId | tempId |
deleteConnectionFromView | viewId, connectionId | |
styleViewObject | viewObjectId | fillColor, lineColor, fontColor, lineWidth, opacity, textAlignment |
styleConnection | connectionId | lineColor, lineWidth, fontColor |
moveViewObject | viewObjectId, x, y | width, height |
createNote | viewId, content | tempId, x, y, width, height |
createGroup | viewId, name | tempId, x, y, width, height |
Complete End-to-End Sequence
This is the canonical workflow for creating a model from scratch:
Step 1: Check Health
curl -s http://localhost:8765/health
Step 2: Query Existing Model (avoid duplicates)
curl -s -X POST http://localhost:8765/model/search \
-H "Content-Type: application/json" \
-d '{"type": "business-actor", "namePattern": "Customer"}'
Step 3: Create Elements
curl -s -X POST http://localhost:8765/model/apply \
-H "Content-Type: application/json" \
-d '{
"changes": [
{"op": "createElement", "type": "business-actor", "name": "Customer", "tempId": "e1"},
{"op": "createElement", "type": "business-service", "name": "Order Processing", "tempId": "e2"},
{"op": "createElement", "type": "application-component", "name": "Order System", "tempId": "e3"}
]
}'
Step 4: Poll for Element IDs
curl -s "http://localhost:8765/ops/status?opId=OPERATION_ID"
Result contains:
{
"status": "complete",
"result": [
{"tempId": "e1", "id": "abc123", "type": "business-actor", "name": "Customer"},
{"tempId": "e2", "id": "def456", "type": "business-service", "name": "Order Processing"},
{"tempId": "e3", "id": "ghi789", "type": "application-component", "name": "Order System"}
]
}
Step 5: Create Relationships (using real IDs)
curl -s -X POST http://localhost:8765/model/apply \
-H "Content-Type: application/json" \
-d '{
"changes": [
{"op": "createRelationship", "type": "serving-relationship", "sourceId": "def456", "targetId": "abc123", "tempId": "r1"},
{"op": "createRelationship", "type": "realization-relationship", "sourceId": "ghi789", "targetId": "def456", "tempId": "r2"}
]
}'
Step 6: Poll for Relationship IDs
curl -s "http://localhost:8765/ops/status?opId=OPERATION_ID_2"
Step 7: Create View (sync — returns immediately)
curl -s -X POST http://localhost:8765/views \
-H "Content-Type: application/json" \
-d '{"name": "Order Processing Overview"}'
Returns: {"id": "view-abc", "name": "Order Processing Overview"}
Step 8: Add Elements to View
curl -s -X POST http://localhost:8765/model/apply \
-H "Content-Type: application/json" \
-d '{
"changes": [
{"op": "addToView", "viewId": "view-abc", "elementId": "abc123", "x": 300, "y": 50, "width": 120, "height": 55, "tempId": "v1"},
{"op": "addToView", "viewId": "view-abc", "elementId": "def456", "x": 300, "y": 200, "width": 120, "height": 55, "tempId": "v2"},
{"op": "addToView", "viewId": "view-abc", "elementId": "ghi789", "x": 300, "y": 350, "width": 120, "height": 55, "tempId": "v3"}
]
}'
Step 9: Poll for Visual Object IDs
The result maps tempId → visual object ID. These are the IDs needed for connections.
Step 10: Add Connections to View
curl -s -X POST http://localhost:8765/model/apply \
-H "Content-Type: application/json" \
-d '{
"changes": [
{"op": "addConnectionToView", "viewId": "view-abc", "relationshipId": "r1-real-id", "sourceVisualId": "v2-visual-id", "targetVisualId": "v1-visual-id"},
{"op": "addConnectionToView", "viewId": "view-abc", "relationshipId": "r2-real-id", "sourceVisualId": "v3-visual-id", "targetVisualId": "v2-visual-id"}
]
}'
Step 11: Auto-Layout
curl -s -X POST http://localhost:8765/views/view-abc/layout \
-H "Content-Type: application/json" \
-d '{"algorithm": "dagre", "options": {"rankdir": "TB", "nodesep": 60, "ranksep": 80}}'
Step 12: Export as PNG (optional)
curl -s -X POST http://localhost:8765/views/view-abc/export \
-H "Content-Type: application/json" \
-d '{"format": "PNG", "scale": 2}'
Step 13: Save Model
curl -s -X POST http://localhost:8765/model/save
Important Gotchas
- •
Connections are NOT auto-created: Adding elements to a view does NOT visualize their relationships. You must explicitly
addConnectionToViewfor each relationship. - •
Poll before using IDs: Never assume an operation is complete. Always poll
/ops/statusand extract real IDs from the result. - •
Visual IDs ≠ concept IDs:
addConnectionToViewneeds the visual object IDs (fromaddToViewresults), not the element/relationship concept IDs. - •
Within-batch tempId references: In a single
/model/applybatch, acreateRelationshipCAN referencetempIdfrom acreateElementin the same batch. ButaddToViewandaddConnectionToViewtypically need IDs from a separate prior batch. - •
Limits: Max 1000 changes per request. 1MB body limit. 200 requests/minute rate limit.
- •
Layout options: Only
dagrealgorithm.rankdir:TB(top-bottom),BT,LR(left-right),RL.nodesep/ranksepin pixels. - •
Connection router:
bendpoint(straight lines) ormanhattan(right-angle routing). Set viaPUT /views/{id}/router. - •
Export formats:
PNGorJPEG. Scale: 0.5 to 4.0. Optionalmarginin pixels. - •
Delete cascade:
deleteElementwithcascade: true(default) removes the element, all its relationships, and all visual references across all views. - •
Search supports regex:
namePatternin/model/searchaccepts regex patterns (e.g.,"^Order.*"to find all elements starting with "Order").
Additional Resources
Reference Files
- •
references/api-operations.md— Detailed field reference for every operation type with full examples - •
references/workflow-templates.md— Reusable multi-step CURL workflows for common modeling scenarios