Skip to main content

Overview

Mutations modify data in Exulu IMP. All mutations follow consistent patterns and return both the modified resource and optional job IDs for background processing.

Mutation patterns

Every resource type (core and dynamic) gets these mutation operations:
OperationPurposeReturns job ID
{typePlural}CreateOneCreate new resourceSometimes
{typePlural}CopyOneByIdDuplicate resourceSometimes
{typePlural}UpdateOneUpdate by filterSometimes
{typePlural}UpdateOneByIdUpdate by IDSometimes
{typePlural}RemoveOneDelete by filterNo
{typePlural}RemoveOneByIdDelete by IDNo
Mutations return a job field when background processing is triggered (e.g., embedding generation, chunk processing).

Create mutations

Pattern

{typePlural}CreateOne(
  input: {type}Input!
  upsert: Boolean
): {type}MutationPayload

type {type}MutationPayload {
  item: {type}!
  job: String  # Job ID if background processing triggered
}

Basic create

mutation {
  agentsCreateOne(
    input: {
      name: "Customer Support Agent"
      description: "Handles customer inquiries"
      backend: "anthropic_claude"
      type: "agent"
    }
  ) {
    item {
      id
      name
      createdAt
    }
  }
}

With upsert

Upsert creates or updates based on unique constraints:
mutation {
  variablesCreateOne(
    input: {
      name: "openai_api_key"
      value: "sk-..."
      encrypted: true
    }
    upsert: true
  ) {
    item {
      id
      name
    }
  }
}

With RBAC

mutation {
  agentsCreateOne(
    input: {
      name: "Private Agent"
      description: "Only accessible by specific users"
      backend: "anthropic_claude"
      type: "agent"
      RBAC: {
        users: [
          { id: "user-123", rights: "write" }
          { id: "user-456", rights: "read" }
        ]
        roles: [
          { id: "developer", rights: "write" }
        ]
      }
    }
  ) {
    item {
      id
      name
      RBAC {
        type
        users {
          id
          rights
        }
      }
    }
  }
}

With job triggering

For contexts with embedders, creating items triggers chunk generation:
mutation {
  documentationCreateOne(
    input: {
      title: "Getting Started Guide"
      url: "https://docs.example.com/start"
      content: "Welcome to our platform..."
      category: "tutorials"
    }
  ) {
    item {
      id
      title
    }
    job  # Job ID for chunk generation
  }
}

Copy mutations

Duplicate an existing resource.

Pattern

{typePlural}CopyOneById(id: ID!): {type}MutationPayload

Example

mutation {
  agentsCopyOneById(id: "agent-123") {
    item {
      id
      name  # Will be "Original Name (Copy)"
      description
    }
  }
}
Copied resources get โ€œ(Copy)โ€ appended to their name and are set to private mode with created_by set to the current user.

Update mutations

Update by ID pattern

{typePlural}UpdateOneById(
  id: ID!
  input: {type}Input!
): {type}MutationPayload

Basic update

mutation {
  agentsUpdateOneById(
    id: "agent-123"
    input: {
      name: "Updated Agent Name"
      description: "New description"
    }
  ) {
    item {
      id
      name
      description
      updatedAt
    }
  }
}

Update with job triggering

For contexts with embedders set to calculateVectors: "onUpdate":
mutation {
  documentationUpdateOneById(
    id: "doc-123"
    input: {
      content: "Updated documentation content..."
    }
  ) {
    item {
      id
      title
    }
    job  # Chunk regeneration job ID
  }
}

Update by filter pattern

{typePlural}UpdateOne(
  where: [Filter{Type}]
  input: {type}Input!
): {type}MutationPayload

Example

mutation {
  agentsUpdateOne(
    where: [
      { name: { eq: "Old Agent Name" } }
    ]
    input: {
      name: "New Agent Name"
    }
  ) {
    item {
      id
      name
    }
  }
}

Update RBAC

mutation {
  agentsUpdateOneById(
    id: "agent-123"
    input: {
      RBAC: {
        users: [
          { id: "user-789", rights: "write" }
        ]
        roles: [
          { id: "admin", rights: "write" }
        ]
      }
    }
  ) {
    item {
      id
      RBAC {
        users {
          id
          rights
        }
        roles {
          id
          rights
        }
      }
    }
  }
}

Delete mutations

Delete by ID pattern

{typePlural}RemoveOneById(id: ID!): {type}

Example

mutation {
  agentsRemoveOneById(id: "agent-123") {
    id
    name
  }
}
For contexts with embedders, deleting an item also deletes all associated chunks automatically.

Delete by filter pattern

{typePlural}RemoveOne(where: JSON!): {type}

Example

mutation {
  agent_sessionsRemoveOne(
    where: { agent: "agent-123" }
  ) {
    id
    name
  }
}

Context-specific mutations

Contexts with embedders, sources, or processors get additional mutations.

Generate chunks

Create vector embeddings for context items.
{contextId}GenerateChunks(
  where: [Filter{Context}]
  limit: Int
): {contextId}GenerateChunksReturnPayload

type {contextId}GenerateChunksReturnPayload {
  message: String!
  items: Int!
  jobs: [String!]
}
Generate for filtered items:
mutation {
  documentationGenerateChunks(
    where: [{ category: { eq: "tutorials" } }]
    limit: 50
  ) {
    message
    items  # Number of items processed
    jobs   # Job IDs
  }
}
Generate for all items:
mutation {
  documentationGenerateChunks {
    message
    items
    jobs
  }
}
Generating all chunks requires super_admin permission.

Delete chunks

Remove vector embeddings while keeping items.
{contextId}DeleteChunks(
  where: [Filter{Context}]
  limit: Int
): {contextId}DeleteChunksReturnPayload

type {contextId}DeleteChunksReturnPayload {
  message: String!
  items: Int!
  jobs: [String!]
}
Example:
mutation {
  documentationDeleteChunks(
    where: [{ category: { eq: "deprecated" } }]
  ) {
    message
    items
  }
}

Execute source

Manually trigger a context source to fetch data.
{contextId}ExecuteSource(
  source: ID!
  inputs: JSON!
): {contextId}ExecuteSourceReturnPayload

type {contextId}ExecuteSourceReturnPayload {
  message: String!
  jobs: [String!]
  items: [String!]
}
Example:
mutation {
  documentationExecuteSource(
    source: "github_docs_sync"
    inputs: {
      repository: "myorg/myrepo"
      branch: "main"
      path: "/docs"
    }
  ) {
    message
    items  # Created item IDs
    jobs   # Job IDs if queued
  }
}
Requires super_admin permission.

Process item

Execute a context processor on a single item.
{contextId}ProcessItem(
  item: ID!
): {contextId}ProcessItemFieldReturnPayload

type {contextId}ProcessItemFieldReturnPayload {
  message: String!
  results: [String]
  jobs: [String]
}
Example:
mutation {
  documentationProcessItem(item: "doc-123") {
    message
    results
    jobs
  }
}

Process items (batch)

Execute processor on multiple items.
{contextId}ProcessItems(
  limit: Int
  filters: [Filter{Context}]
  sort: SortBy
): {contextId}ProcessItemFieldReturnPayload
Example:
mutation {
  documentationProcessItems(
    limit: 10
    filters: [{ category: { eq: "api" } }]
  ) {
    message
    results
    jobs
  }
}
Processing mutations require super_admin permission.

Workflow mutations

Run workflow

Execute a workflow template with variables.
mutation {
  runWorkflow(
    id: "workflow-123"
    variables: {
      customerName: "John Doe"
      orderId: "12345"
      priority: "high"
    }
  ) {
    result   # Last message from workflow
    job      # Job ID if queued
    metadata # Execution metadata (tokens, duration)
  }
}
Response:
{
  "data": {
    "runWorkflow": {
      "result": {
        "role": "assistant",
        "content": "Order #12345 has been processed for John Doe."
      },
      "job": "job-abc-123",
      "metadata": {
        "tokens": {
          "totalTokens": 1234,
          "inputTokens": 234,
          "outputTokens": 1000
        },
        "duration": 2345
      }
    }
  }
}

Schedule workflow

Create a cron-based workflow schedule.
mutation {
  upsertWorkflowSchedule(
    workflow: "workflow-123"
    schedule: "0 9 * * *"  # Daily at 9 AM
  ) {
    status
    job
  }
}
Cron schedule examples:
  • 0 9 * * * - Daily at 9:00 AM
  • */15 * * * * - Every 15 minutes
  • 0 0 * * 0 - Weekly on Sunday at midnight
  • 0 0 1 * * - Monthly on the 1st at midnight

Delete workflow schedule

mutation {
  deleteWorkflowSchedule(workflow: "workflow-123") {
    status
  }
}

Evaluation mutations

Run evaluation

Execute an evaluation set on an agent.
mutation {
  runEval(
    id: "eval-run-123"
    test_case_ids: ["test-1", "test-2", "test-3"]
  ) {
    jobs   # Job IDs for each test case
    count  # Number of jobs created
  }
}
Run all test cases in eval set:
mutation {
  runEval(id: "eval-run-123") {
    jobs
    count
  }
}
Evaluations are always run as background jobs using BullMQ.

Queue management mutations

Pause queue

mutation {
  pauseQueue(queue: eval_runs) {
    success
  }
}

Resume queue

mutation {
  resumeQueue(queue: eval_runs) {
    success
  }
}

Drain queue

Remove all waiting/delayed jobs from queue.
mutation {
  drainQueue(queue: eval_runs) {
    success
  }
}
Draining a queue removes jobs that havenโ€™t started yet. Active jobs continue running.

Delete job

Remove a specific job from queue.
mutation {
  deleteJob(
    queue: eval_runs
    id: "job-abc-123"
  ) {
    success
  }
}

User management mutations

Create user

mutation {
  usersCreateOne(
    input: {
      name: "John Doe"
      email: "john@example.com"
      password: "secure-password"
      role: "role-id-123"
      type: "user"
    }
  ) {
    item {
      id
      name
      email
      role
    }
  }
}
Creating users requires super_admin permission. Passwords are automatically hashed with bcrypt.

Create API user

mutation {
  usersCreateOne(
    input: {
      email: "api@example.com"
      type: "api"
      apikey: "sk_abc123def456.../production-api-key"
      role: "api-role-id"
    }
  ) {
    item {
      id
      email
      type
    }
  }
}
API keys must be generated using ExuluDatabase.api.key.generate() and are stored as bcrypt hashes.

Update user role

mutation {
  usersUpdateOneById(
    id: "user-123"
    input: {
      role: "new-role-id"
    }
  ) {
    item {
      id
      name
      role
    }
  }
}

Grant super admin

mutation {
  usersUpdateOneById(
    id: "user-123"
    input: {
      super_admin: true
    }
  ) {
    item {
      id
      name
      super_admin
    }
  }
}
Granting or revoking super_admin requires existing super_admin permission.

Role management mutations

Create role

mutation {
  rolesCreateOne(
    input: {
      name: "Developer"
      agents: "write"
      evals: "write"
      workflows: "read"
      variables: "read"
      users: "read"
      api: "read"
    }
  ) {
    item {
      id
      name
    }
  }
}
Permission levels:
  • "read" - Can view resources
  • "write" - Can view and modify resources

Update role permissions

mutation {
  rolesUpdateOneById(
    id: "role-123"
    input: {
      agents: "write"
      workflows: "write"
    }
  ) {
    item {
      id
      name
      agents
      workflows
    }
  }
}

Advanced mutation patterns

Mutation with variables

mutation CreateAgent(
  $name: String!
  $description: String
  $backend: String!
) {
  agentsCreateOne(
    input: {
      name: $name
      description: $description
      backend: $backend
      type: "agent"
    }
  ) {
    item {
      id
      name
    }
  }
}
Variables:
{
  "name": "Support Agent",
  "description": "Handles customer support",
  "backend": "anthropic_claude"
}

Multiple mutations in one request

mutation {
  agent1: agentsCreateOne(
    input: {
      name: "Agent 1"
      backend: "openai"
      type: "agent"
    }
  ) {
    item {
      id
      name
    }
  }

  agent2: agentsCreateOne(
    input: {
      name: "Agent 2"
      backend: "anthropic"
      type: "agent"
    }
  ) {
    item {
      id
      name
    }
  }
}

Mutation with fragment

fragment AgentResult on agent {
  id
  name
  description
  provider
  modelName
  createdAt
}

mutation {
  agentsCreateOne(
    input: {
      name: "New Agent"
      backend: "anthropic"
      type: "agent"
    }
  ) {
    item {
      ...AgentResult
    }
  }
}

Error handling

Validation errors

{
  "errors": [
    {
      "message": "Field 'name' is required",
      "path": ["agentsCreateOne"]
    }
  ]
}

Permission errors

{
  "errors": [
    {
      "message": "Insufficient permissions to edit this record",
      "path": ["agentsUpdateOneById"]
    }
  ]
}

Not found errors

{
  "errors": [
    {
      "message": "Record not found",
      "path": ["agentsUpdateOneById"]
    }
  ]
}

RBAC errors

{
  "errors": [
    {
      "message": "Only the creator can edit this private record",
      "path": ["agentsUpdateOneById"]
    }
  ]
}

Transaction handling

GraphQL mutations in Exulu IMP are not wrapped in database transactions by default. If you need transactional behavior, implement it at the application level.

Best practices

Check job status - When mutations return a job field, use the jobs query to monitor background processing status
Handle RBAC carefully - When updating RBAC, provide the complete desired state. Partial updates replace existing permissions.
Never expose API keys - API keys and passwords are write-only. Theyโ€™re hashed and cannot be retrieved.
Use upsert for idempotency - When you need idempotent operations, use upsert: true with unique constraints

Next steps