Synapse HTTP/REST API
Many components within the Synapse ecosystem provide HTTP/REST APIs to provide a portable interface. Some of these APIs are RESTful, while other (streaming data) APIs are technically not.
HTTP/REST API Conventions
All Synapse RESTful APIs use HTTP GET/POST methods to retrieve and modify data. All POST requests expect a JSON body. Each RESTful API call will return a result wrapper dictionary with one of two conventions.
For a successful API call:
{"status": "ok", "result": "some api result here"}
or for an unsuccessful API call:
{"status": "err": "code": "ErrCodeString", "mesg": "A human friendly message."}
Streaming HTTP API endpoints, such as the interface provided to retrieve nodes from a Synapse Cortex, provide JSON results via HTTP chunked encoding where each chunk is a single result.
The client example code in these docs is given with the Python “aiohttp” and “requests” modules. They should be enough to understand the basic operation of the APIs.
For additional examples, see the code examples at HTTPAPI Examples.
Authentication
Most Synapse HTTP APIs require an authenticated user. HTTP API endpoints requiring authentication may be accessed using either HTTP Basic authentication via the HTTP “Authorization” header, using an API Key with the “X-API-KEY” header, or as part of an authenticated session.
API Key Support
A Cortex user can create their own API key via Storm. The following is an example of generating a user API key:
storm> ($key, $info)= $lib.auth.users.byname($lib.user.name()).genApiKey('Test Key') $lib.print($key) XauBgBIUKgWJEm7VyvkmcuaGZbIl6M2nmueWjRtnYtA=
This API Key can then be used to make HTTP API calls. The following example shows
the use of curl
and jq
to make a Storm call with the API key and then format
the response:
$ curl -k -s -H "X-API-KEY: XauBgBIUKgWJEm7VyvkmcuaGZbIl6M2nmueWjRtnYtA=" \ --data '{"query": "return($lib.user.name())"}' \ https://localhost:4443/api/v1/storm/call | jq { "status": "ok", "result": "root" }
/api/v1/login
The login API endpoint may be used to create an authenticated session. To create and use an
authenticated session, the HTTP client library must support cookies. This session may then be
used to call other HTTP API endpoints as the authenticated user. This expects a user
and
passwd
provided in the body of a POST
request. The reusable session cookie is returned
in a Set-Cookie
header.
Both of the Python examples use session managers which manage the session cookie automatically.
import aiohttp
async def logInExample(ssl=False):
async with aiohttp.ClientSession() as sess:
info = {'user': 'visi', 'passwd': 'secret'}
async with sess.post('https://localhost:4443/api/v1/login', json=info, ssl=ssl) as resp:
item = await resp.json()
if item.get('status') != 'ok':
code = item.get('code')
mesg = item.get('mesg')
raise Exception(f'Login error ({code}): {mesg}')
# we are now clear to make additional HTTP API calls using sess
import requests
def logInExample(ssl=False):
sess = requests.session()
url = 'https://localhost:4443/api/v1/login'
info = {'user': 'visi', 'passwd': 'secret'}
resp = sess.post(url, json=info, verify=ssl)
item = resp.json()
if item.get('status') != 'ok':
code = item.get('code')
mesg = item.get('mesg')
raise Exception(f'Login error ({code}): {mesg}')
# we are now clear to make additional HTTP API calls using sess
/api/v1/logout
The logout API endpoint may be used to end an authenticated session. This invalidates the session, and any further requests to authenticated endpoints will fail on authentication failed errors.
Both of the Python examples use session managers which manage the session cookie automatically.
import aiohttp
def logoutExample(sess, ssl):
url = 'https://localhost:4443/api/v1/logout'
resp = sess.get(url, ssl=ssl)
item = resp.json()
if item.get('status') != 'ok':
code = item.get('code')
mesg = item.get('mesg')
raise Exception(f'Logout error ({code}): {mesg}')
import requests
def logoutExample(sess, ssl):
url = 'https://localhost:4443/api/v1/logout'
resp = sess.get(url, verify=ssl)
item = resp.json()
if item.get('status') != 'ok':
code = item.get('code')
mesg = item.get('mesg')
raise Exception(f'Logout error ({code}): {mesg}')
/api/v1/active
- Method
GET
This is an unauthenticated API that returns the leader status of Cell.
- Returns
A dictionary with the
active
key set to True or False.
/api/v1/auth/users
- Method
GET
- Returns
A list of dictionaries, each of which represents a user on the system.
/api/v1/auth/roles
- Method
GET
- Returns
A list of dictionaries, each of which represents a role on the system.
/api/v1/auth/adduser
- Method
POST
This API endpoint allows the caller to add a user to the system.
- Input
This API expects the following JSON body:
{ "name": "myuser" }
Any additional “user dictionary” fields (other than “iden”) may be specified.
- Returns
The newly created user dictionary.
/api/v1/auth/addrole
- Method
POST
This API endpoint allows the caller to add a role to the system.
- Input
This API expects the following JSON body:
{ "name": "myrole" }
Any additional “role dictionary” fields (other than “iden”) may be specified.
- Returns
The newly created role dictionary.
/api/v1/auth/delrole
- Method
POST
This API endpoint allows the caller to delete a role from the system.
- Input
This API expects the following JSON body:
{ "name": "myrole" }
- Returns
null
/api/v1/auth/user/<id>
- Method
POST
This API allows the caller to modify specified elements of a user dictionary.
- Input
This API expects a JSON dictionary containing any updated values for the user.
- Returns
The updated user dictionary.
- Method
GET
This API allows the caller to retrieve a user dictionary.
- Returns
A user dictionary.
/api/v1/auth/password/<id>
- Method
POST
This API allows the caller to change a user’s password. The authenticated user must either be an admin or the user whose password is being changed.
- Input
This API expects a JSON dictionary containing the key
passwd
with the new password string.- Returns
The updated user dictionary.
/api/v1/auth/role/<id>
- Method
POST
This API allows the caller to modify specified elements of a role dictionary.
- Input
This API expects a dictionary containing any updated values for the role.
- Returns
The updated role dictionary.
- Method
GET
This API allows the caller to retrieve a role dictionary.
- Returns
A role dictionary.
/api/v1/auth/grant
- Method
POST
This API allows the caller to grant a role to a given user.
- Input
This API expects the following JSON body:
{ "user": "<id>", "role": "<id>" }
- Returns
The updated user dictionary.
/api/v1/auth/revoke
- Method
POST
This API allows the caller to revoke a role which was previously granted to a user.
- Input
This API expects the following JSON body:
{ "user": "<id>", "role": "<id>" }
- Returns
The updated user dictionary.
Cortex
A Synapse Cortex implements an HTTP API for interacting with the hypergraph and data model. Some of the provided APIs are pure REST APIs for simple data model operations and single/simple node modification. However, many of the HTTP APIs provided by the Cortex are streaming APIs which use HTTP chunked encoding to deliver a stream of results as they become available.
The Cortex also implements the Axon HTTP API. Permissions are checked within the Cortex, and then the request is executed on the Axon.
/api/v1/feed
The Cortex feed API endpoint allows the caller to add nodes in bulk.
- Method
POST
- Input
The API expects the following JSON body:
{ "items": [ <node>, ... ], # and optionally... "view": <iden>, }
Each
<node>
is expected to be in packed tuple form:[ [<formname>, <formvalu>], {...} ]
- Returns
The API returns
{"status": "ok", "result": null}
on success and any failures are returned using the previously mentioned REST API convention.
/api/v1/storm
The Storm API endpoint allows the caller to execute a Storm query on the Cortex and stream back the messages generated during the Storm runtime execution. In addition to returning nodes, these messages include events for node edits, tool console output, etc. This streaming API has back-pressure, and will handle streaming millions of results as the reader consumes them. For more information about Storm APIs, including opts behavior, see Storm API Guide.
- Method
GET
- Input
The API expects the following JSON body:
{ "query": "a storm query here", # optional "opts": { ... } # optional "stream": "jsonlines" }
- Returns
The API returns a series of messages generated by the Storm runtime. Each message is returned as an HTTP chunk, allowing readers to consume the resulting messages as a stream.
The
stream
argument to the body modifies how the results are streamed back. Currently this optional argument can be set tojsonlines
to get newline separated JSON data.- Examples
The following two examples show querying the
api/v1/storm
endpoint and receiving multiple message types.aiohttp example:
import json import pprint # Assumes sess is an aiohttp client session that has previously logged in query = '.created $lib.print($node.repr(".created")) | limit 3' data = {'query': query, 'opts': {'repr': True}} url = 'https://localhost:4443/api/v1/storm' async with sess.get(url, json=data) as resp: async for byts, x in resp.content.iter_chunks(): if not byts: break mesg = json.loads(byts) pprint.pprint(mesg)
requests example:
import json import pprint # Assumes sess is an requests client session that has previously logged in query = '.created $lib.print($node.repr(".created")) | limit 3' data = {'query': query, 'opts': {'repr': True}} url = 'https://localhost:4443/api/v1/storm' resp = sess.get(url, json=data, stream=True) for chunk in resp.iter_content(chunk_size=None, decode_unicode=True): mesg = json.loads(chunk) pprint.pprint(mesg)
When working with these APIs across proxies, we have experienced issues with NGINX interfering with the chunked encoding. This may require more careful message reconstruction. The following shows using aiohttp to do that message reconstruction.
import json import pprint # Assumes sess is an requests client session that has previously logged in query = '.created $lib.print($node.repr(".created")) | limit 3' data = {'query': query, 'opts': {'repr': True}} url = 'https://localhost:4443/api/v1/storm' async with sess.get(url, json=data) as resp: buf = b"" async for byts, chunkend in resp.content.iter_chunks(): if not byts: break buf += byts if not chunkend: continue mesg = json.loads(buf) buf = b"" pprint.pprint(buf)
/api/v1/storm/call
The Storm Call API endpoint allows the caller to execute a Storm query on the Cortex and get a single return
value back from the runtime. This is analogous to using the callStorm()
Telepath API. This expects to return a
value from the Storm query using the Storm return( )
syntax.
For more information about Storm APIs, including opts behavior, see Storm API Guide.
- Method
GET
- Input
The API expects the following JSON body:
{ "query": "a storm query here", # optional "opts": { ... } }
- Returns
The API returns
{"status": "ok", "result": return_value}
on success and any failures are returned using the previously mentioned REST API convention.- Examples
The following two examples show querying the
api/v1/storm/call
endpoint and receiving a return value.aiohttp example:
import pprint # Assumes sess is an aiohttp client session that has previously logged in query = '$foo = $lib.str.format("hello {valu}", valu="world") return ($foo)' data = {'query': query} url = 'https://localhost:4443/api/v1/storm/call' async with sess.get(url, json=data) as resp: info = await resp.json() pprint.pprint(info)
requests example:
import pprint # Assumes sess is an requests client session that has previously logged in query = '$foo = $lib.str.format("hello {valu}", valu="world") return ($foo)' data = {'query': query} url = 'https://localhost:4443/api/v1/storm/call' resp = sess.get(url, json=data) info = resp.json() pprint.pprint(info)
/api/v1/storm/nodes
Warning
This API is deprecated in Synapse v2.110.0
and will be removed in a future version.
The Storm nodes API endpoint allows the caller to execute a Storm query on the Cortex and stream back the resulting nodes. This streaming API has back-pressure, and will handle streaming millions of results as the reader consumes them.
- Method
GET
- Input
See /api/v1/storm for expected JSON body input.
- Returns
The API returns the resulting nodes from the input Storm query. Each node is returned as an HTTP chunk, allowing readers to consume the resulting nodes as a stream.
Each serialized node will have the following structure:
[ [<form>, <valu>], # The [ typename, typevalue ] definition of the node. { "iden": <hash>, # A stable identifier for the node. "tags": {}, # The tags on the node. "props": {}, # The node's secondary properties. # optionally (if query opts included {"repr": True} "reprs": {} # Presentation values for props which need it. } ]
The
stream
argument, documented in the /api/v1/storm endpoint, modifies how the nodes are streamed back. Currently this optional argument can be set tojsonlines
to get newline separated JSON data.
/api/v1/storm/export
The Storm export API endpoint allows the caller to execute a Storm query on the Cortex and export the resulting nodes
in msgpack format such that they can be directly ingested with the syn.nodes
feed function.
- Method
GET
- Input
See /api/v1/storm for expected JSON body input.
- Returns
The API returns the resulting nodes from the input Storm query. This API yields nodes after an initial complete lift in order to limit exported edges.
Each exported node will be in msgpack format.
There is no Content-Length header returned, since the API cannot predict the volume of data a given query may produce.
/api/v1/model
- Method
GET
This API allows the caller to retrieve the current Cortex data model.
- Input
The API takes no input.
- Returns
The API returns the model in a dictionary, including the types, forms and tagprops. Secondary property information is also included for each form:
{ "types": { ... # dictionary of type definitions }, "forms": { ... # dictionary of form definitions, including secondary properties }, "tagprops": { ... # dictionary of tag property definitions } }
/api/v1/model/norm
- Method
GET, POST
This API allows the caller to normalize a value based on the Cortex data model. This may be called via a GET or POST requests.
- Input
The API expects the following JSON body:
{ "prop": "prop:name:here", "value": <value>, }
- Returns
The API returns the normalized value as well as any parsed subfields or type specific info:
{ "norm": <value>, "info": { "subs": {}, ... } }
/api/v1/storm/vars/get
- Method
GET
This API allows the caller to retrieve a storm global variable.
- Input
The API expects the following JSON body:
{ "name": "varnamehere", "default": null, }
- Returns
The API returns the global variable value or the specified default using the REST API convention described earlier.
/api/v1/storm/vars/set
- Method
POST
This API allows the caller to set a storm global variable.
- Input
The API expects the following JSON body:
{ "name": "varnamehere", "value": <value>, }
- Returns
The API returns true using the REST API convention described earlier.
/api/v1/storm/vars/pop
- Method
POST
This API allows the caller to pop/delete a storm global variable.
- Input
The API expects the following JSON body:
{ "name": "varnamehere", "default": <value>, }
- Returns
The API returns the current value of the variable or default using the REST API convention described earlier.
/api/v1/core/info
- Method
GET
This API allows the caller to retrieve the current Cortex version, data model definitions, and Storm information.
- Input
The API takes no input.
- Returns
The API returns the model in a dictionary, including the types, forms and tagprops. Secondary property information is also included for each form:
{ "version": [ <major>, <minor>, <patch> ], # Version tuple "modeldict": { ... # dictionary of model definitions }, "stormdocs": { "libraries": [ ... # list of information about Storm libraries. ], "types": [ ... # list of information about Storm types. ] } }
/api/ext/*
This API endpoint is used as the Base URL for Extended HTTP API endpoints which are user defined. See Extended HTTP API for additional information about this endpoint.
Aha
A Synapse Aha service implements an HTTP API for assisting with devops.
/api/v1/aha/provision/service
- Method
POST
This API allows the caller to generate an AHA provisioning URL.
- Input
The API expects the following JSON body:
{ "name": " ... name of the service being provisioned", "provinfo": { "dmon:port": # optional integer, default Telepath listening port. "https:port": # optional integer, default HTTPS listening port. "mirror": # optional string, service to Mirror. "conf": { ... # optional, default service configuration values. } } }
- Returns
The API returns the following provisioning information. The data is returned using the REST API convention described earlier:
{ "url": "< the AHA provisioning URL >", }
/api/v1/aha/services
- Method
GET
This API allows the caller to get a list of all the registered services.
- Input
The API accepts the following optional JSON body:
{ "network": " ... name of the aha network to list", }
- Returns
The API returns the following provisioning information. The data is returned using the REST API convention described earlier:
[ { "name": "< the full service name >", "svcname": "< service name part >", "svcnetw": "< service network part >", "svcinfo": { "run": "< runtime service identifier >", "iden": "< persistent service identifier >", "leader": "< service leader name >", "urlinfo": { "scheme": "< listening scheme >", "port": listening port, "path": "< listening path >", "host": "< listening IP address >" }, "ready": < boolean indicating the service is either an active leader or in the realtime change event window >, "online": < runtime aha identifier if the service is connected > } }, ... ]
Axon
A Synapse Axon implements an HTTP API for uploading and downloading files. The HTTP APIs use HTTP chunked encoding for handling large files.
/api/v1/axon/files/del
This API allows the caller to delete multiple files from the Axon by the SHA-256.
- Method
POST
- Input
The API expects the following JSON body:
{ "sha256s": [<sha256>, ...], }
- Returns
The API returns an array of SHA-256 and boolean values representing whether each was found in the Axon and deleted. The array is returned using the REST API convention described earlier.
/api/v1/axon/files/put
This API allows the caller to upload and save a file to the Axon. This may be called via a PUT or POST request.
- Method
PUT, POST
- Input
The API expects a stream of byte chunks.
- Returns
On successful upload, or if the file already existed, the API returns information about the file:
{ "md5": "<the md5sum value of the uploaded bytes>", "sha1": "<the sha1 value of the uploaded bytes>", "sha256": "<the sha256 value of the uploaded bytes>", "sha512": "<the sha512 value of the uploaded bytes>", "size": <the size of the uploaded bytes> }
/api/v1/axon/files/has/sha256/<SHA-256>
This API allows the caller to check if a file exists in the Axon as identified by the SHA-256.
- Method
GET
- Returns
True if the file exists; False if the file does not exist.
/api/v1/axon/files/by/sha256/<SHA-256>
This API allows the caller to retrieve or remove a file from the Axon as identified by the SHA-256. If the file does not exist a 404 will be returned.
- Method
GET
- Returns
If the file exists a stream of byte chunks will be returned to the caller. A
Range
header with a singlebytes
value can be provided to get a subset of a file.
- Method
HEAD
- Returns
If the file exists, the
Content-Length
header will be set for the size of the file. If aRange
header with a singlebytes
value is provided, theContent-Length
header will describe the size of the range, and theContent-Range
header will also be set to describe the range of the requested bytes.
- Method
DELETE
- Returns
Boolean via the REST API convention described earlier. If the file is not found an error is returned.