emem Edges API

The edges API from emem — 2 operation(s) for edges.

Operations 2

POST /v1/edges Persist temporal knowledge-graph edges. #
POST /v1/edges/recall Recall temporal knowledge-graph edges in either direction, bi-temporally… #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/emem-dev-edges-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

emem-dev-edges-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  description: emem is shared memory for AI agents working together in the real world.
  license:
    name: Apache-2.0
  title: emem Edges API
  version: 2.4.0
  x-emem-surface-asymmetry:
    memory_notes: MCP only
    reach_them_at: POST /mcp, method tools/call
    read_side_is_here:
    - /v1/memory/search
    - /v1/memory/sse
    - /memories/{path}
    tools:
    - emem_memory_create
    - emem_memory_view
    - emem_memory_delete
    - emem_memory_rename
    - emem_memory_str_replace
    - emem_memory_supersede
    why_not_here: These write the agent correspondence plane, which is prose and untrusted-by-declaration. It is deliberately not part of the REST fact surface, and the two planes are kept apart rather than merged for convenience.
servers:
- description: Hosted instance (HTTPS-only)
  url: https://emem.dev
tags:
- name: edges
paths:
  /v1/edges:
    post:
      description: 'Persist temporal knowledge-graph edges. Body is a signed Attestation envelope whose `edges[]` array carries each edge {subj, pred, obj, valid_from, valid_to?, confidence, signer, signed_at, schema_cid?, note?}. The edge leaves are folded into the merkle root so the signature commits to them. Additive: an attestation with no edges behaves exactly as /v1/attest.'
      operationId: emem_edges_write
      requestBody:
        content:
          application/json:
            schema:
              properties:
                edges:
                  items:
                    properties:
                      confidence:
                        type: number
                      note:
                        type: string
                      obj:
                        description: object fact CID
                        type: string
                      pred:
                        type: string
                      subj:
                        description: subject fact CID
                        type: string
                      valid_from:
                        type: integer
                      valid_to:
                        type: integer
                    required:
                    - subj
                    - pred
                    - obj
                    - valid_from
                    - confidence
                    - signer
                    - signed_at
                    type: object
                  type: array
              required:
              - facts
              - edges
              - batch_root
              - attester
              - signature
              type: object
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
          description: ok
        default:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: 'error, the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`.'
      summary: Persist temporal knowledge-graph edges.
      tags:
      - edges
  /v1/edges/recall:
    post:
      description: 'Read temporal knowledge-graph edges (subj --pred--> obj, valid over [valid_from, valid_to)), bi-temporally filtered, in EITHER direction. Forward (`subj`, direction="out", the default): edges originating at a subject fact. Reverse (`obj`, direction="in"): edges pointing AT a fact, what disagrees-with / supersedes / relates-to it. Returns a signed list of edges plus the distinct neighbour fact CIDs (`objs` for out, `subjs` for in); the receipt commits the returned edge CIDs into its signature preimage.


        When to use: Call this to read the typed CONNECTIONS of a fact, what disagrees with it, what superseded it, what relates to it, as of a point in time. A plain recall gives you the fact; this gives you how that fact links to others in the memory graph. Ask it when the user says ''what is this related to'', ''what replaced this observation'', ''why is this value contested'', or ''what did this place''s relations look like as of date X''. Pick a direction: set `subj` (direction="out") to ask ''what does this fact point at''; set `obj` (direction="in") to ask the REVERSE, ''what disagrees-with / supersedes / points-at this fact''. Set exactly one of subj/obj, an ambiguous or empty request errors honestly rather than returning a silent empty. Pass `as_of_tslot` to get the latest edge per neighbour whose valid interval covers that moment (newer edges shadow older, nothing is deleted); pass `pred` (e.g. `disagrees_with`, `supersedes`) to filter, or omit it (empty string) for every predicate. Tip: a quicker way to get a fact + its outbound edges in one shot is `emem_recall` with include:["edges"]. Follow each edge''s `obj`/`subj` with `emem_fetch` to resolve the related fact, or `emem_verify_receipt` to confirm the signature offline.'
      operationId: emem_edges_recall
      requestBody:
        content:
          application/json:
            schema:
              properties:
                as_of_tslot:
                  description: valid-time bound; latest edge per neighbour whose interval covers it
                  type: integer
                direction:
                  description: out (default)=subj→objs; in=obj→subjs; inferred from which of subj/obj is set when omitted
                  enum:
                  - out
                  - in
                  type: string
                limit:
                  default: 100
                  maximum: 1000
                  minimum: 1
                  type: integer
                obj:
                  description: 'object fact CID (reverse / direction=in): what points at this fact'
                  type: string
                pred:
                  description: predicate filter; empty string scans all predicates
                  type: string
                subj:
                  description: subject fact CID (forward / direction=out); set exactly one of subj/obj
                  type: string
              type: object
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedResponse'
          description: ok
        default:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: 'error, the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`.'
      summary: Recall temporal knowledge-graph edges in either direction, bi-temporally…
      tags:
      - edges
components:
  schemas:
    Cost:
      description: 'Self-declared cost block on every receipt. Honest accounting: latencies are observed, freshness is the age of the stalest source cited (null when undatable, never 0 as a stand-in), `was_cached` is true when the hot cache served the read.'
      properties:
        credits:
          description: Conceptual cost units; 0 for L0/L1 read endpoints on the hosted responder.
          type: number
        latency_p50_ms:
          type: number
        latency_p99_ms:
          type: number
        source_freshness_s:
          description: 'Age of the STALEST source this response cites: now minus the earliest captured_at across the returned facts'' sources. null when nothing in the response carries a dated source, which is the honest answer for a primitive that reads no observation. Was a hardcoded 0 until 2026-08-05, so a 2021 DEM tile reported as 0 s old; a null here means unknown, never fresh.'
          type:
          - integer
          - 'null'
        was_cached:
          type: boolean
      type: object
    Fact:
      description: A primary attestation at (cell, band, tslot). `value` is the band's typed reading (number, array of numbers for vector bands, or a categorical class id). `unit` is the band's declared unit (e.g. `m_msl`, `degC`, `mm`).
      properties:
        absence_reason:
          description: Present only when kind=`absence`.
          enum:
          - unavailable_capability
          - outside_coverage
          - archetype_seed_unavailable
          - gpu_unavailable
          - upstream_error
          - upstream_timeout
          type: string
        band:
          type: string
        cell:
          $ref: '#/components/schemas/Cell64'
        fact_cid:
          $ref: '#/components/schemas/FactCid'
        kind:
          description: '`primary` = signed measurement; `absence` = signed "we don''t have this here" with a typed reason.'
          enum:
          - primary
          - absence
          type: string
        provenance:
          description: Upstream source key (e.g. `copdem30m`, `s2_l2a`, `cams_eu`).
          type: string
        receipt:
          $ref: '#/components/schemas/Receipt'
        tslot:
          $ref: '#/components/schemas/Tslot'
        unit:
          type: string
        value:
          description: Number, array of numbers, or class id depending on band type.
      required:
      - kind
      - cell
      - band
      - tslot
      - value
      - fact_cid
      - receipt
      type: object
    MaterializeNote:
      description: 'One entry in the response''s `materialize_notes[]`, recording what the lazy materializer did during this call. status:"materialized" means a signed fact was minted and persisted (a Primary observation OR a confirmed, evidence-backed Absence - both are signed and citeable by fact_cid). status:"skipped" means nothing was signed: `reason_class` says why (transient `timeout`/`upstream_error`, retryable; or structural `unknown_band`/`no_materializer`/`capability_unavailable`, not retryable here) and `absence` is always false, because a skip is ''unknown'', never a confirmed absence.'
      properties:
        absence:
          description: Always false on a skip; a confirmed absence is a signed fact with status:materialized, not a skip.
          type: boolean
        band:
          type: string
        cell:
          $ref: '#/components/schemas/Cell64'
        fact_cid:
          type: string
        latency_ms:
          type: number
        ok:
          type: boolean
        reason:
          type: string
        reason_class:
          enum:
          - timeout
          - upstream_error
          - unknown_band
          - no_materializer
          - capability_unavailable
          type: string
        retryable:
          type: boolean
        status:
          enum:
          - materialized
          - skipped
          type: string
      type: object
    ErrorEnvelope:
      description: The `emem.error.v1` failure envelope returned by every endpoint on a 4xx/5xx. Branch on the stable `code` (not the human `message`). See GET /v1/errors for the full code catalog.
      properties:
        code:
          description: Stable machine-readable error code. One of the codes in GET /v1/errors.
          example: invalid_argument
          type: string
        details:
          description: Optional structured recovery hints; present on errors that ship machine-readable next-steps.
          type: object
        message:
          description: Human-readable detail. For invalid_argument this names the offending field (e.g. "missing field `q`").
          type: string
        path:
          description: Request path that produced the error.
          example: /v1/ask
          type: string
        schema:
          const: emem.error.v1
          type: string
      required:
      - code
      - message
      - schema
      type: object
    PubKey:
      description: Ed25519 32-byte public key, base32-nopad-lowercase encoded (52 chars). Returned in receipts and `/.well-known/emem.json`.
      example: 777er3yihgifqmv5hmc2wwmyszgddzderzhsx6rex4yoakwomvka
      type: string
    Cell64:
      description: 'cell64 wire form: four base-65,536 bigrams separated by dots, e.g. `defi.zb4d9.pefa.zf619`. Encoded resolution is ~9.55 m at the equator. Each bigram is either a CVCV quad, consonant `[bcdfghjklmnpqrstvwxyz]` followed by vowel `[aeiouAEIOU]` repeated twice, OR a synthetic 5-char `z[0-9a-f]{4}` slot used for the unused pad cells in the 65,536-entry alphabet. The regex pin matches `pattern` below byte-for-byte and is also surfaced under `Cell64Pattern` so agents can validate before sending.'
      example: defi.zb4d9.pefa.zf619
      maxLength: 23
      minLength: 19
      pattern: ^(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})(?:\.(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})){3}$
      type: string
    SignedResponse:
      description: Standard recall envelope. `facts` is the array of signed facts touched by this call (subset of `bands_already_attested_at_cell` after auto-materialization). `receipt` is the responder's signature over the call. `materialize_notes` lists any lazy-materializer activity that happened to satisfy the request, empty for purely warm reads.
      properties:
        bands_already_attested_at_cell:
          description: Bands the cell already has facts for, regardless of whether they were requested. Useful for follow-up calls without a second /v1/coverage_matrix hit.
          items:
            type: string
          type: array
        caveats:
          description: Plain-language constraints the caller should fold into their answer (grid resolution, revisit cadence, sample-size warnings).
          items:
            type: string
          type: array
        facts:
          items:
            $ref: '#/components/schemas/Fact'
          type: array
        materialize_notes:
          items:
            $ref: '#/components/schemas/MaterializeNote'
          type: array
        receipt:
          $ref: '#/components/schemas/Receipt'
      required:
      - facts
      - receipt
      type: object
    FactCid:
      description: 'Content id of a fact: base32-nopad-lowercase encoding of `blake3(canonical_cbor(fact))`, the FULL 32-byte digest with no truncation. Always 52 characters, alphabet `[a-z2-7]`. A cid of any other length is a damaged citation, not a shorter address: /v1/memory_token/resolve rejects it as `fact_cid_malformed_length` rather than guessing. Note that `entity_cid` and `bundle_cid` are NOT this shape; both truncate to 16 bytes (26 characters) and hash an identity anchor or a citation list rather than a complete body.'
      example: qtv2bco56qw4pmlohk56dotoxyl3atmnjpmzrijj2kazw2mj57oq
      maxLength: 52
      minLength: 52
      pattern: ^[a-z2-7]{52}$
      type: string
    Tslot:
      description: Band-tempo-relative integer offset from the emem epoch. Each band declares its tempo (`fast` / `medium` / `slow` / `static`); tslot is the rounded count of that tempo's unit since the epoch.
      minimum: 0
      type: integer
    Receipt:
      description: 'Ed25519-signed receipt. The browser-side verifier at /verify reconstructs the preimage from the receipt fields alone, no callback to the issuer. **A receipt is byte-for-byte or nothing.** Current receipts carry `preimage_version: 2`, whose preimage binds request_id, served_at, primitive, cells, fact_cids AND, when present, the scope / as_of / edges / source_versions / field digests and the `merkle_proof` segment. Reshaping a receipt — dropping a field an SDK considers redundant, re-keying it, summarising it, round-tripping it through a lossy model — invalidates the signature BY DESIGN, and the result is indistinguishable on the wire from tampering. Store and forward the responder''s exact bytes. POST /v1/verify_receipt names which of the two it is where it can prove the difference (`reason: receipt_reshaped_after_signing` with a `failure_detail`). What is NOT signed: the caller''s `place`/`q` string, raw `lat`/`lng`, requested `bands[]`, requested `tslot`, and `intent` — a wrong-place geocode produces a valid signature for the wrong cell. Branch on /v1/locate `selected.is_high_confidence` before trusting place-anchored answers. Also: `fact_cid` is per-replica (signed_at differs across responders even for byte-identical upstream pixels); cross-replica join key is the tuple (cell, band, tslot). /v1/recall_polygon emits one independently signed receipt per cell under `by_cell.<cell>.receipt`, `merged_facts[]` is convenience flattening and is NOT covered by an aggregate signature.'
      properties:
        cells:
          items:
            $ref: '#/components/schemas/Cell64'
          type: array
        cost:
          $ref: '#/components/schemas/Cost'
        fact_cids:
          items:
            $ref: '#/components/schemas/FactCid'
          type: array
        intent:
          description: Optional natural-language hint. Populated when served via /v1/intent.
          type: string
        merkle_proof:
          description: 'Inclusion proof for `fact_cids[0]` when persisted. Omitted from JSON when the cited facts pre-date the proof tree; under preimage_version 2 that absence is itself signed (an explicit ABSENT marker), so it is a statement rather than a gap. Do not strip this field: v2 binds it into the signature and removing it makes an authentic receipt report `signature_valid: false`.'
          properties:
            leaf_index:
              description: u32 leaf index in the canonical-sorted batch.
              type: integer
            path:
              description: Sibling hashes leaf→root.
              items:
                description: 32-byte sibling hash as a byte array
                items:
                  type: integer
                type: array
              type: array
            root:
              description: The expected 32-byte batch root as a byte array.
              items:
                type: integer
              type: array
            version:
              description: 'Merkle hashing rule: 0 (omitted) = legacy unprefixed, 1 = RFC 6962-style prefixed.'
              type: integer
          required:
          - leaf_index
          - path
          - root
          type: object
        primitive:
          description: 'Namespaced wire form: `emem.recall`, `emem.find_similar`, `emem.verify`, …'
          type: string
        registry_cid:
          description: CID of the function registry version in force.
          type: string
        request_id:
          description: ULID generated per request.
          type: string
        responder:
          $ref: '#/components/schemas/PubKey'
        responder_key_epoch:
          description: u32 rotation counter; bumps when the operator rotates keys.
          type: integer
        responder_pubkey_b32:
          $ref: '#/components/schemas/PubKey'
        schema_cid:
          description: CID of the active CDDL profile.
          type: string
        served_at:
          description: ISO 8601 UTC, second precision.
          type: string
        signature:
          description: Ed25519 signature, 64 bytes base32-nopad-lowercase encoded.
          type: string
        source_versions:
          additionalProperties:
            type: string
          description: Per-source freshness map.
          type: object
      required:
      - request_id
      - served_at
      - primitive
      - cells
      - fact_cids
      - schema_cid
      - responder
      - responder_key_epoch
      - responder_pubkey_b32
      - signature
      - registry_cid
      type: object