Guest Machines

Invoke an agent

Prepare for programmatic execution using stable inputs and observable runs.

Programmatic invocation should behave like calling a typed function: provide valid input, receive an execution identity, and observe the run until it reaches a terminal state.

Define a stable contract

Before integrating, make the agent's required input and output explicit. Avoid parsing prose when a structured schema can represent the result.

The caller should know:

  • which agent or published interface it is invoking;
  • the required input shape;
  • whether execution is synchronous, streamed, or asynchronous;
  • how to observe and cancel the run;
  • which terminal states are possible.

Know what an output schema guarantees

Agent and team contracts use a bounded, object-rooted profile of JSON Schema Draft 2020-12. A configured schema must be a JSON object whose root declares "type": "object". The $schema keyword is optional; when present, it must identify Draft 2020-12. $ref and $dynamicRef may point to definitions inside the same schema with a # reference. Guest Machines never retrieves a remote schema.

An otherwise successful model response must be one exact, finite JSON document. Surrounding prose, Markdown fences, trailing content, duplicate object keys, and non-finite numbers such as NaN do not count as structured output. Validation uses JSON types exactly: it does not turn strings into numbers, remove unknown fields, or insert missing fields. In particular, JSON Schema's default keyword is documentation and does not supply a value. Additional object properties are allowed by JSON Schema unless your contract sets "additionalProperties": false.

Format assertions are enforced for color, date, date-time, duration, email, hostname, idn-email, idn-hostname, ipv4, ipv6, iri, iri-reference, json-pointer, regex, relative-json-pointer, time, uri, uri-reference, uri-template, and uuid. An unknown format makes the schema invalid instead of silently becoming an annotation.

Invalid or unbounded schemas are rejected when the agent or team is saved. The profile allows at most:

  • 64 KiB of canonical schema JSON;
  • 32 levels of schema nesting;
  • 2,048 schema nodes;
  • 512 declared properties across the schema;
  • 2,048 characters in one description; and
  • 1,024 characters in one pattern.

Provider-native structured output helps the model produce the right shape, but it is not the verifier. Guest Machines validates the final result against the original contract, including constraints that a model provider could not enforce while generating.

Treat execution and verification as separate outcomes. status=completed means the execution lifecycle finished; it does not prove that the returned result is correct. Inspect verification_status as well:

  • verified means every required platform check passed;
  • unverified means execution completed without evidence from a required verifier;
  • verification_failed means at least one required check failed;
  • not_evaluated means no verification result exists yet, including when execution stops before a verifier can run.

Output-schema verification runs only after execution has produced an otherwise successful terminal result. A schema mismatch changes the run to status=failed with verification_status=verification_failed; it is not a completed but partially valid result. A pause, cancellation, provider outage, tool failure, or other operational failure stops before this check and remains not_evaluated.

When file delivery is part of the contract, set input_data.require_artifact=true. A run that creates at least one downloadable file passes the required artifact_requirement check. If it finishes without a file, the run fails with artifact.required_not_published and verification_failed. If the agent also has an output_schema, the result-format and file checks are both required and both must pass.

Verification is scoped to the checks that ran. Passing output-schema validation proves that the JSON structure met the declared contract. It does not prove that its facts are true, that its reasoning is sound, or that it completed the requested task well.

Failed checks report property paths and constraint names without exposing the rejected output values.

Provide files to a run

The product UI calls these files. In the API, each file has an artifact record. Upload a new artifact once, then reference its ID from an agent, team, or pipeline start. Existing ready artifacts can be referenced the same way without uploading their bytes again.

Upload a new artifact

The upload body is the file itself: application/octet-stream, not JSON, base64, or multipart form data. Declare its exact byte length and lowercase SHA-256 digest. This Bash example calculates both values, uploads the bytes, and captures the immutable artifact identity returned by the 201 response:

set -euo pipefail
: "${GSMC_TOKEN:?Set GSMC_TOKEN to a Guest Machines bearer token}"

file_path="./dataset.csv"
file_name="$(basename "$file_path")"
file_size="$(wc -c < "$file_path" | tr -d '[:space:]')"
file_sha256="$(openssl dgst -sha256 "$file_path" | awk '{print $NF}')"
upload_key="$(uuidgen | tr '[:upper:]' '[:lower:]')"

artifact_json="$(
  curl --fail-with-body --silent --show-error \
    --request POST \
    --url "https://api.guestmachines.com/api/v1/artifacts" \
    --url-query "file_name=${file_name}" \
    --header "Authorization: Bearer ${GSMC_TOKEN}" \
    --header "Content-Type: application/octet-stream" \
    --header "Idempotency-Key: ${upload_key}" \
    --header "X-Upload-Size: ${file_size}" \
    --header "X-Content-SHA256: ${file_sha256}" \
    --data-binary "@${file_path}"
)"

artifact_id="$(jq -r '.id' <<< "$artifact_json")"
artifact_sha256="$(jq -r '.sha256' <<< "$artifact_json")"

curl supplies Content-Length for --data-binary. It is optional—chunked uploads are supported—but when a client sends it, it must agree with X-Upload-Size. The server verifies the declared byte count before returning 201, so the returned artifact is already ready. Keep upload_key stable only while retrying this exact filename, description, size, and checksum.

A delegated token is locked to the organization that issued it, and a service principal is likewise organization-bound; neither needs X-Org-Id. A direct human session can use that header to select one of the user's active memberships. A user-session or delegated-token upload is private to that user. A service-principal upload is shared with its organization because a service principal has no user owner.

A service principal that has a resource allowlist cannot call this upload operation. Use a service principal without a resource allowlist, or a delegated token when the upload should belong privately to its user. Giving an allowlisted principal access to the target agent does not allow uploads.

Attach the returned artifact

Put the returned artifact_id into the next start request. Pin the same digest with expected_sha256 so admission fails if your client selected different bytes than it intended:

: "${AGENT_ID:?Set AGENT_ID to the agent UUID}"
run_key="$(uuidgen | tr '[:upper:]' '[:lower:]')"

jq -n \
  --arg artifact_id "$artifact_id" \
  --arg sha256 "$artifact_sha256" \
  '{
    input_data: {task: "Analyze the attached dataset"},
    artifact_inputs: [{
      artifact_id: $artifact_id,
      alias: "dataset",
      expected_sha256: $sha256
    }]
  }' |
  curl --fail-with-body --silent --show-error \
    --request POST \
    --url "https://api.guestmachines.com/api/v1/agents/${AGENT_ID}/run" \
    --header "Authorization: Bearer ${GSMC_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Idempotency-Key: ${run_key}" \
    --data-binary @-

The upload needs runs:create. Attaching it needs runs:read in addition to the target's invocation scope (agents:invoke in this example). Grant runs:read as well if the caller will poll or inspect the run.

Reuse an existing artifact

The same artifact_inputs shape accepts a ready artifact created by an earlier run:

{
  "input_data": {
    "task": "Analyze the attached dataset"
  },
  "artifact_inputs": [
    {
      "artifact_id": "10000000-0000-4000-8000-000000000001",
      "alias": "dataset",
      "expected_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    }
  ]
}

Use List artifacts to find ready uploads and run outputs visible to the caller, or list a run's artifacts when the producing run is known. expected_sha256 is optional, but including it prevents the new run from starting if it does not match the immutable bytes your client selected.

Resource-allowlisted service principals have a deliberately narrower artifact view. Their artifact lists omit every run-produced output. They can list, read, download, preview, delete, or attach a caller upload only when that exact artifact UUID is in the principal's resource allowlist. Allowlisting an agent, run, or other related resource is not enough, and artifact_inputs admission does not bypass this check.

The alias is the short name the receiving run uses to find the file. It does not rename the original file. Every alias in one request must be different.

Before starting the new run, Guest Machines confirms that the caller can read the artifact and gives the new run access to it. Its contents are not automatically added to the prompt or model context; the agent chooses whether to read the file.

Providing or reading a file does not prove that the run used it correctly or that the result is correct, and does not add an outcome-verification check. A caller upload is input, not run-produced output, so it cannot satisfy require_artifact.

List a run's artifact bindings returns the files provided to a run and their safe source details. Its default scope=inputs excludes files created by the receiving run; use scope=all when debugging every file the run could access.

This reuse mechanism applies only to files. Structured outputs remain pass-by-value: submit the saved JSON object as the next run's input_data, or use ordinary pipeline input/output mappings. An authorized client can retrieve the complete object from Retrieve a run. There is no generic API reference that lets one run open another run's structured output directly.

Scope the machine caller

Use a service principal with the minimum invoke and run-observation scopes. A successful authentication does not bypass resource visibility or allowlist rules: the agent being invoked must be shared with the organization, or explicitly granted to the principal, because a service principal can never reach a private resource. See authentication.

If the agent can ask a user a question or request tool approval, use a delegated token instead. Grant runs:read to retrieve the pending interaction and runs:edit to answer it. The same delegated user who started the run must make that decision; service principals and administrator overrides cannot. See the HITL API flow.

Make starts retry-safe

For endpoints that accept Idempotency-Key, generate one stable value per intended execution. Reuse it only when retrying the same payload. Reusing a key with different input should be treated as a conflict.

Attribute the spend

In a shared organization, send project_tag, ticket_tag, and cost_center_tag alongside the run's input to record what the work belonged to. Each is optional and at most 255 characters; blank values are stored as none. They are a reporting dimension only — no budget, limit, or approval reads them — and they surface in the runs list filters and the organization's chargeback export.

Set them on the call that starts the work. Child runs inherit from their parent, so a fan-out or a pipeline reports as one unit, and the values are fixed once the run starts. An integration that fires the same job repeatedly is usually better served by a schedule or an inbound webhook, which carries the tags in its own configuration rather than repeating them per call.

Where the details are

Run an agent documents the request and response, the failure modes, and the scope it needs, with request samples in seven languages. Runs covers observing execution to a terminal state, retrieving files created by runs, and inspecting the API records for files provided to a run.

Team and pipeline invocation use the same artifact_inputs request shape. Service-principal keys are supported, but runs started by them have no acting user. Use a delegated token when a workflow depends on user-only tools such as ask_user or send_email.

First-party SDKs are not published yet. Operation identifiers in the reference are frozen, so a client generated from the contract keeps working.

On this page