Platform API

Base URL  https://api.marc27.com
Checking…

What this is

MARC27 is infrastructure for materials science. One API gives you a materials knowledge graph built from published literature, semantic search and retrieval over that corpus, GPU compute for simulation and model inference, and a marketplace of models and datasets — with provenance recorded on every result.

Everything below is reachable over plain HTTPS with a single credential. Responses are JSON. Long-running work (ingestion, compute, agent runs) is submitted once and polled or streamed over Server-Sent Events.

Authentication

Every endpoint under /api/v1 needs a credential unless noted otherwise. /health and this page do not.

API key — for scripts, agents and CI
HeaderX-API-Key: m27_…
LifetimeDoes not expire until you delete it
How to get onePOST /api/v1/api-keys (needs a token first), or create one in the dashboard at platform.marc27.com
Bearer token — for browser sessions and the CLI
HeaderAuthorization: Bearer <token>
How to get onePOST /api/v1/auth/token with your sign-in token, or run the device flow from POST /api/v1/auth/device/start
ExpiryShort-lived. Rotate with POST /api/v1/auth/refresh; an expired token answers token_expired

Your first call

Confirm you can reach the API. No credential needed:

curl https://api.marc27.com/health

Now prove your credential works — this returns your own profile:

curl https://api.marc27.com/api/v1/users/me \
  -H "X-API-Key: m27_your_key_here"

And a first real query — semantic search across the materials corpus:

curl https://api.marc27.com/api/v1/knowledge/search \
  -H "X-API-Key: m27_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "creep resistance in nickel superalloys", "limit": 5}'

GraphQL

One request can span the knowledge graph, platform resources and compute. Send queries to POST /api/v1/graphql with the same credential as REST; opening /api/v1/graphql in a browser gives you an interactive playground.

curl https://api.marc27.com/api/v1/graphql \
  -H "X-API-Key: m27_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ graphStats { nodes edges entityTypes } }"}'
24 queries
search(term: String!, limit: Int! = 20): [Entity!]!
entity(name: String!): Entity
paths(from: String!, to: String!, maxHops: Int! = 3): [[String!]!]!
graphStats: GraphStats!
me: PlatformUser
marketplace(resourceType: String, limit: Int! = 50, offset: Int! = 0): [MarketplaceResource!]!
resource(slug: String!): MarketplaceResource
corpora(domain: String, kind: String, limit: Int! = 50): [CorpusInfo!]!
billingBalance: BillingBalance!
agentRuns(limit: Int! = 20): [AgentRunInfo!]!
agentRun(id: String!): AgentRunInfo
jobs(status: String, limit: Int! = 20): [JobInfo!]!
computeGpus: [GpuOfferingGql!]!
computeJob(id: String!): ComputeJobGql
computeEstimate(input: ComputeJobInput!): CostEstimateGql!
embeddingStats: EmbeddingStats!
provenance(entityType: String!, entityId: String!): [ProvenanceRecord!]!
ingestJob(id: String!): IngestJobGql
semanticSearch(query: String!, limit: Int! = 5, corpusId: String): [SearchResult!]!
recall(query: String!, limit: Int! = 5, factLimit: Int! = 25): RecallResult!
llmModels(provider: String, limit: Int! = 50): [LlmModel!]!
deployments(status: String, limit: Int! = 20): [Deployment!]!
discourseSpecs(limit: Int! = 20): [DiscourseSpec!]!
nodes(status: String, limit: Int! = 50): [Node!]!
6 mutations
submitComputeJob(input: ComputeJobInput!): ComputeJobGql!
cancelComputeJob(id: String!): Boolean!
submitIngestJob(input: IngestJobInput!): IngestJobGql!
createDeployment(projectId: String!, name: String!, image: String!, gpuType: String, target: String, budgetMaxUsd: Float): Deployment!
stopDeployment(id: String!): Boolean!
createDiscourseSpec(slug: String!, yaml: String!): DiscourseSpec!

Endpoints

Paths are shown in full. Anything taking a body expects Content-Type: application/json.

Health

3 endpoints

Liveness, readiness and runtime metrics. No credential required.

GET/health
Liveness check
GET/health/ready
Readiness check
GET/health/metrics
Prometheus-style metrics

Authentication

7 endpoints

Exchange a sign-in token for a platform token, or run the CLI device flow.

POST/api/v1/auth/token
Exchange Supabase JWT for platform JWT
{"supabase_token":"..."}
POST/api/v1/auth/refresh
Refresh platform token
{"refresh_token":"m27r_..."}
POST/api/v1/auth/logout
Revoke a refresh token
{"refresh_token":"m27r_..."}
POST/api/v1/auth/device/start
Start device flow login
{"client_id":"prism"}
POST/api/v1/auth/device/poll
Poll device flow authorization status
{"device_code":"..."}
POST/api/v1/auth/device/approve
Approve a device flow code
{"user_code":"ABCD-EFGH","supabase_token":"eyJhbGciOi..."}
POST/api/v1/auth/device/deny
Deny a device flow code
{"user_code":"ABCD-EFGH","supabase_token":"eyJhbGciOi..."}

Users

2 endpoints

The profile behind the current credential.

GET/api/v1/users/me
Get current user profile
PATCH/api/v1/users/me
Update profile
{"display_name":"Dr. Smith","orcid_id":"0000-0001"}

Organisations

9 endpoints

Organisations own projects, billing and membership.

GET/api/v1/orgs
List organizations
POST/api/v1/orgs
Create an organization
{"name":"My Lab","slug":"my-lab"}
GET/api/v1/orgs/{id}
Get organization details
PATCH/api/v1/orgs/{id}
Update an organization
{"name":"New Lab Name"}
DELETE/api/v1/orgs/{id}
Delete an organization
GET/api/v1/orgs/{id}/members
List organization members
POST/api/v1/orgs/{id}/members
Add an organization member
{"user_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","role":"member"}
PATCH/api/v1/orgs/{id}/members/{user_id}
Update an organization member role
{"role":"admin"}
DELETE/api/v1/orgs/{id}/members/{user_id}
Remove an organization member

Projects

34 endpoints

A project scopes data, credentials and spend. Most work happens inside one.

GET/api/v1/projects
List all user's projects
POST/api/v1/projects
Create a project
{"name":"My Project","slug":"my-project","org_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}
GET/api/v1/projects/{id}
Get project details
PATCH/api/v1/projects/{id}
Update a project
{"name":"New Project Name"}
DELETE/api/v1/projects/{id}
Delete a project
GET/api/v1/projects/{id}/jobs
List jobs for a project
GET/api/v1/projects/{project_id}/collab/members
List project members
POST/api/v1/projects/{project_id}/collab/members
Add a project member
{"user_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","role":"contributor"}
PUT/api/v1/projects/{project_id}/collab/members/{user_id}
Update a project member role
{"role":"admin"}
DELETE/api/v1/projects/{project_id}/collab/members/{user_id}
Remove a project member
POST/api/v1/projects/{project_id}/collab/invite
Create a project invite
{"email":"[email protected]","role":"viewer"}
GET/api/v1/projects/{project_id}/collab/invites
List project invites
GET/api/v1/projects/{project_id}/collab/activity
List project activity
POST/api/v1/projects/{project_id}/knowledge/activate
Activate a corpus for a project
{"corpus_slug":"materials-project"}
GET/api/v1/projects/{project_id}/knowledge/activations
List project corpus activations
DELETE/api/v1/projects/{project_id}/knowledge/activations/{activation_id}
Deactivate a project corpus activation
GET/api/v1/projects/{project_id}/llm/models
List LLM models
POST/api/v1/projects/{project_id}/llm/stream
LLM completion (SSE stream)
{"model":"claude-sonnet-4","messages":[{"role":"user","content":"..."}]}
GET/api/v1/projects/{project_id}/llm-keys
List LLM key hints for a project
POST/api/v1/projects/{project_id}/llm-keys
Store an encrypted LLM API key
{"provider":"openai","key":"sk-..."}
DELETE/api/v1/projects/{project_id}/llm-keys/{provider}
Delete an LLM API key
POST/api/v1/projects/{project_id}/llm-keys/{provider}/rotate
Rotate an encrypted LLM API key
{"key":"sk-..."}
GET/api/v1/projects/{project_id}/mcp-services
List MCP service instances
GET/api/v1/projects/{project_id}/mcp-services/{instance_id}
Get MCP service instance details
POST/api/v1/projects/{project_id}/mcp-services/{instance_id}/proxy
Proxy a request to an MCP service
{"path":"/tools/list","body":{}}
POST/api/v1/projects/{project_id}/mcp-services/{instance_id}/scale
Scale an MCP service instance
{"replicas":1}
POST/api/v1/projects/{project_id}/conversations
Create a conversation in a project
{"id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","title":"Alloy screening","metrics_json":{}}
GET/api/v1/projects/{project_id}/conversations
List a project's conversations
PUT/api/v1/projects/{id}/auto-route
Set the project's model auto-routing posture
{"enabled":true}
GET/api/v1/projects/{project_id}/data-access/classes
List the project's data-class labels
GET/api/v1/projects/{project_id}/data-access/grants
List data-class grants (project admin)
POST/api/v1/projects/{project_id}/data-access/grants
Grant a subject access to a data class (project admin)
{"subject_type":"user","subject_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","data_class":"restricted","access":"read"}
DELETE/api/v1/projects/{project_id}/data-access/grants
Revoke a subject's data-class grant (project admin)
{"subject_type":"user","subject_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","data_class":"restricted"}
GET/api/v1/projects/{project_id}/data-access/me
Your effective data access in this project

Invitations

1 endpoint

Accept an invitation to someone else's project.

POST/api/v1/invites/accept
Accept an invite token
{"token":"..."}

API keys

3 endpoints

Long-lived credentials for scripts, agents and CI.

GET/api/v1/api-keys
List API keys
POST/api/v1/api-keys
Create an API key
{"project_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","scopes":["read","write"]}
DELETE/api/v1/api-keys/{id}
Delete an API key

Knowledge

24 endpoints

The materials knowledge graph, semantic search over the corpus, and document ingestion.

GET/api/v1/knowledge/graph/stats
Graph node/edge counts
GET/api/v1/knowledge/graph/search?q=titanium&limit=5
Fulltext entity search
GET/api/v1/knowledge/graph/entity/{entity_name}
Entity + neighbors
GET/api/v1/knowledge/graph/paths
Find paths between graph entities
POST/api/v1/knowledge/graph/seed
Seed graph data
{"source_url":"..."}
POST/api/v1/knowledge/graph/ingest
Ingest graph entities and relationships
{"entities":[],"relationships":[]}
POST/api/v1/knowledge/admin/reconcile-vector-schema
Admin: drop + re-create vector indexes at the configured dimension
POST/api/v1/knowledge/admin/embed-graph
Admin: backfill assertion embeddings
POST/api/v1/knowledge/search
Semantic vector search
{"query":"creep resistant superalloy","limit":5}
POST/api/v1/knowledge/embed
Embed text into vector store
{"doc_id":"my-doc","content":"..."}
POST/api/v1/knowledge/embed/bulk
Bulk embed documents into vector store
{"documents":[]}
GET/api/v1/knowledge/embeddings/stats
Embedding store statistics
GET/api/v1/knowledge/catalog
List all corpora
GET/api/v1/knowledge/catalog/{slug}
Get corpus details
POST/api/v1/knowledge/ingest-job
Submit an ingest job
{"corpus_id":"...","source_url":"..."}
GET/api/v1/knowledge/ingest-job/{job_id}
Get ingest job status
GET/api/v1/knowledge/ingest-jobs
List ingest jobs
POST/api/v1/knowledge/research/query
RLM research query (SSE stream)
{"question":"...","depth":1}
POST/api/v1/knowledge/research/web-search
Run a web-backed research search
{"query":"..."}
GET/api/v1/knowledge/capabilities
Knowledge service tool list
POST/api/v1/knowledge/recall
Recall the passages and facts relevant to a question
POST/api/v1/knowledge/ingest/holistic/stream
Ingest a document end to end, streaming progress
POST/api/v1/knowledge/ingest/holistic/upload
Upload a document for end-to-end ingestion
POST/api/v1/knowledge/research/search-web
Web search for research (alias of /research/web-search)

Compute

14 endpoints

Submit GPU work, estimate its cost before committing, then follow it to completion.

GET/api/v1/compute/gpus
List available GPUs + pricing
GET/api/v1/compute/providers
List compute providers
POST/api/v1/compute/submit
Submit a compute job
{"image":"my-model","gpu_type":"A100-80GB","inputs":{}}
POST/api/v1/compute/estimate
Estimate job cost
{"image":"my-model","gpu_type":"A100-80GB","inputs":{}}
GET/api/v1/compute/{job_id}
Get job status
GET/api/v1/compute/{job_id}/logs
Get job logs
POST/api/v1/compute/{job_id}/cancel
Cancel a job
GET/api/v1/compute/deployments
List model deployments
POST/api/v1/compute/deployments
Create a model deployment
{"name":"my-model","image":"repo/image:tag","target":"prism_node"}
GET/api/v1/compute/deployments/{id}
Get model deployment details
DELETE/api/v1/compute/deployments/{id}
Stop a model deployment
GET/api/v1/compute/deployments/{id}/health
Run a deployment health check
ANY/api/v1/compute/deployments/{id}/invoke
Forward an inference request to the deployment (any method)
ANY/api/v1/compute/deployments/{id}/invoke/{*path}
Sub-path passthrough, e.g. .../invoke/v1/chat/completions

Marketplace

20 endpoints

Published models, datasets and tools, with versions and download grants.

GET/api/v1/marketplace?status=approved
List marketplace resources
GET/api/v1/marketplace/resources
Same as above (alias)
GET/api/v1/marketplace/search?q=titanium
Search resources
POST/api/v1/marketplace
Publish a resource
{"resource_type":"model","name":"...","slug":"..."}
GET/api/v1/marketplace/{slug}
Get marketplace resource
PATCH/api/v1/marketplace/{slug}
Update marketplace resource
{"description":"Updated description"}
DELETE/api/v1/marketplace/{slug}
Delete marketplace resource
POST/api/v1/marketplace/{slug}/submit
Submit marketplace resource for review
POST/api/v1/marketplace/{slug}/ontology
Upload and validate raw YAML for a draft ontology package
ontology_id: my-ontology\nversion: 1.0.0\nclasses: []
POST/api/v1/marketplace/{slug}/purchase
Purchase an approved ontology package and select it for a project
{"project_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}
GET/api/v1/marketplace/{slug}/versions
List marketplace resource versions
POST/api/v1/marketplace/{slug}/versions
Create marketplace resource version
{"version":"1.0.0"}
POST/api/v1/marketplace/{slug}/download
Record marketplace resource download
POST/api/v1/marketplace/grants
Grant project access to a resource
{"project_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","resource_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7"}
DELETE/api/v1/marketplace/grants/{project_id}/{resource_id}
Revoke project access to a resource
POST/api/v1/marketplace/licensed-sources/resolve
Resolve one entitled, coverage-validated licensed source (CALPHAD TDB, property dataset, instrument data) for the caller's project; refuses with the named gap instead of substituting
{"source_type":"thermodynamic_database","required_coverage":{"elements":["Mo","Nb"],"systems":[["Mo","Nb"]],"property_domains":[]}}
GET/api/v1/marketplace/resources/{slug}
Get a marketplace resource (alias)
GET/api/v1/marketplace/resources/{slug}/install
Time-limited download URL for a resource artifact (alias)
GET/api/v1/marketplace/{slug}/install
Time-limited download URL for a resource artifact
POST/api/v1/marketplace/find
Semantic search over published resources
{"query":"creep-resistant superalloy dataset","types":[],"limit":5}

Jobs

4 endpoints

Generic platform job submission and lifecycle.

POST/api/v1/jobs
Submit a queue-backed platform job
{"job_type":"simulation","project_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","payload":{}}
GET/api/v1/jobs/{id}
Get platform job details
POST/api/v1/jobs/{id}/cancel
Cancel a platform job
GET/api/v1/jobs/{id}/events
Stream platform job events

Research

1 endpoint

One synchronous call that starts a research run and blocks until it answers.

POST/api/v1/research
Run research synchronously and return one answer document
{"question":"Which superalloys resist creep above 1000 C?"}

Conversations

5 endpoints

Server-side chat history, scoped to a project.

GET/api/v1/conversations/{id}
Get a conversation
PATCH/api/v1/conversations/{id}
Update a conversation's title or metrics
{"title":"Alloy screening v2"}
DELETE/api/v1/conversations/{id}
Delete a conversation
POST/api/v1/conversations/{id}/messages
Append messages to a conversation
{"messages":[{"id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","ordinal":0,"role":"user","content":"..."}]}
GET/api/v1/conversations/{id}/messages
List a conversation's messages

Chat

1 endpoint

Grounded chat answered against the corpus.

POST/api/v1/chat
Chat grounded in the knowledge corpus
{"message":"Which superalloys resist creep above 1000 C?"}

Discourse

6 endpoints

Multi-agent deliberation: register a spec, run it, read the turns.

GET/api/v1/discourse/specs
List discourse specs
POST/api/v1/discourse/specs
Create a discourse spec
{"slug":"my-discourse","yaml":"..."}
GET/api/v1/discourse/specs/{id}
Get discourse spec
POST/api/v1/discourse/run/{spec_id}
Run a discourse instance
{"inputs":{}}
GET/api/v1/discourse/{instance_id}
Get discourse instance
GET/api/v1/discourse/{instance_id}/turns
List discourse turns

Workflows

6 endpoints

Declarative multi-step pipelines and their instances.

GET/api/v1/workflows
List workflow instances
POST/api/v1/workflows
Start a workflow
{"spec":"my-workflow","inputs":{}}
GET/api/v1/workflows/specs
List workflow specs
POST/api/v1/workflows/specs
Register a workflow spec
{"spec_yaml":"...","access":"public"}
GET/api/v1/workflows/{id}
Get workflow instance
POST/api/v1/workflows/{id}/cancel
Cancel workflow instance

Nodes

11 endpoints

Register and reach compute nodes, including key exchange for encrypted transfer.

GET/api/v1/nodes
List registered nodes
GET/api/v1/nodes/live
List currently connected nodes
GET/api/v1/nodes/connect
Connect compute node WebSocket
POST/api/v1/nodes/register
Register a node (REST)
{"name":"my-node","capabilities":{}}
GET/api/v1/nodes/{node_id}
Get node details
DELETE/api/v1/nodes/{id}
Deregister a node
POST/api/v1/nodes/{node_id}/heartbeat
Node heartbeat
GET/api/v1/nodes/{node_id}/public-key
Get node E2EE public key
POST/api/v1/nodes/{node_id}/exchange-key
E2EE key exchange
{"public_key":"base64..."}
POST/api/v1/nodes/spin-up
Bring a compute node into a project in one call
{"name":"lab-a100","target":"own-hardware"}
POST/api/v1/nodes/{node_id}/tools/{tool}/invoke
Invoke a tool hosted on a remote node
{"args":{}}

External data

1 endpoint

Brokered access to external materials databases using platform credentials.

POST/api/v1/data/materials-project
Query Materials Project through platform credentials
{"formula":"Ni3Al","properties":["formation_energy_per_atom"]}

Conductor

1 endpoint

Model selection and orchestration over the platform's model pool.

POST/api/v1/mird
Conductor: pick and orchestrate models for a task

Policy

1 endpoint

Evaluate an action against the platform's access policy.

POST/api/v1/policy/evaluate
Check if an action is allowed (RBAC + data classification). The classification is read from the resource's provenance, not from this body; without a resolvable resource_id the request is refused.
{"action":"read_data","resource":{"resource_type":"corpus","resource_id":"ntrs:19650021522"}}

Events

1 endpoint

Server-Sent Events tail of the durable platform event stream.

GET/api/v1/events/subscribe
Server-Sent Events tail of the platform event stream

Usage

5 endpoints

What has been consumed, by user, project or individual run.

GET/api/v1/usage/user
Get usage metrics
GET/api/v1/usage/me
Get current usage metrics
GET/api/v1/usage/projects/{project_id}
Get project usage
GET/api/v1/usage/projects/{project_id}/entries
List project usage ledger entries
GET/api/v1/usage/runs/{run_id}
Total cost of a single run

Billing

9 endpoints

Credit balance, prices, top-ups and per-category spend allowances.

GET/api/v1/billing/balance?project_id={project_id}
Get the balance of the org billed for a project (omit project_id for the legacy fallback)
GET/api/v1/billing/usage?period=monthly
Usage breakdown by service/model
GET/api/v1/billing/history?page=1&per_page=50
Paginated transaction ledger
GET/api/v1/billing/prices
Current credit pricing table (public)
GET/api/v1/billing/packages
Available credit packages for purchase
POST/api/v1/billing/topup
Create checkout for the org that owns a project
{"package":"starter","project_id":"00000000-0000-0000-0000-000000000000"}
GET/api/v1/billing/allowances
Your org's monthly spend caps and spend to date
PUT/api/v1/billing/allowances
Set a per-org monthly spend cap (org admin)
{"org_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","category":"compute","monthly_credits":50000}
POST/api/v1/billing/subscribe
Start checkout for the hosted-node subscription

Support

4 endpoints

Raise and track support tickets.

GET/api/v1/support/tickets
List support tickets
POST/api/v1/support/tickets
Create a ticket
{"description":"...","type":"bug_report"}
GET/api/v1/support/tickets/{ticket_id}
Get support ticket
PATCH/api/v1/support/tickets/{ticket_id}
Update support ticket
{"status":"resolved"}

Install

1 endpoint

What a fresh install can do, read anonymously.

GET/api/v1/install/manifest
What a fresh install can do, read anonymously

Federation

1 endpoint

The platform's public key, so nodes can verify each other without calling home.

GET/api/v1/federation/platform-pubkey
The platform's Ed25519 public key, readable without a credential

GraphQL

2 endpoints

One endpoint spanning graph, platform and compute. See the schema above.

POST/api/v1/graphql
GraphQL query
{"query":"{ graphStats { nodes edges } }"}
GET/api/v1/graphql
GraphQL endpoint guide

Agent discovery

1 endpoint

The machine-readable version of this page.

GET/api/v1/agent/capabilities
Full endpoint and capability map

account

6 endpoints
GET/api/v1/account/storage/opt-in
Is cloud storage of your PRISM account data on, and since when
POST/api/v1/account/storage/opt-in
Turn cloud storage of your PRISM account data on or off (default off; turning it off does not delete what is already stored)
{"enabled":true}
PUT/api/v1/account/storage/object
Upload one file of your PRISM account data (body = raw bytes, ?path=ontology/store.db)
GET/api/v1/account/storage/object
Download one file of your PRISM account data (?path=ontology/store.db)
GET/api/v1/account/storage/list
List your stored PRISM account data (?path= narrows to a sub-path)
POST/api/v1/account/storage/delete
Delete one file of your PRISM account data
{"path":"ontology/store.db"}

agents

6 endpoints
POST/api/v1/agents
Start a research agent run; returns immediately with a run id
{"question":"How does rhenium affect creep in nickel superalloys?"}
GET/api/v1/agents
List your agent runs, newest first
GET/api/v1/agents/{id}
Get a run's state and, once finished, its answer
GET/api/v1/agents/{id}/events
Stream run events as SSE; resume with ?after_seq=N
GET/api/v1/agents/{id}/ws
Stream the same run events over a WebSocket
POST/api/v1/agents/{id}/cancel
Cancel a running agent run

Errors

Failures return a JSON body shaped {"error": {"code": "…", "message": "…"}} with a matching HTTP status. A 404 also carries a suggestions array naming the endpoints you most likely meant, so a mistyped path tells you how to fix itself.

Live status

Core services
API
This endpoint
operational
Database
Readiness probe
checking…
Documented endpoints
Public REST surface