.. index:: PVX API ================= Using the PVX API ================= Introduction ============ The PVX API exposes multiple functions for the following tasks: * querying the collected data, * managing the user interface (including dashboards and widgets), * configuring the software. To access most of these functions, you will need some sort of token (such as an API key or a Session ID). See :ref:`API Keys `. In this documentation, ```` refers to the hostname or IP address of the server hosting the API. This URI is the root of the currently available protocols: - :ref:`HTTP`: .. code-block:: text https:///api/ - :ref:`WebSocket`: .. code-block:: text wss:///api/ws The documentation of the API functions is available at: .. code-block:: text https:///api/doc .. warning:: The PVX API is served **over TLS** (hence the **https://** and **wss://** prefixes). For security reasons, it is **strongly** recommended that you install your own SSL certificate into PVX instead of using the pre-installed one. For more information on security considerations, please read the :ref:`related section `. Response Format of API Calls ============================ The API responses are formatted as `JSON `_. The response is composed of messages. A single message is returned, except when streaming is enabled (see below). A message contains a ``type`` attribute: .. code-block:: text { "type": "", "": , "": , "": , ... } When a request succeeds, a message of type ``result`` is returned. .. code-block:: text { "type": "result", "result": } When a request fails, a message of type ``error`` is returned. The errors are meant to be self-explanatory. For instance, calling a function with less arguments than expected will return something similar to: .. code-block:: text { "type": "error", "error": "TOO-FEW-ARGUMENTS", "names": ["title", "queries"], "count": 2 } The API is also able to stream chunks instead of returning a single response. This method is described in :ref:`API Streaming`. Querying the API ================ The ``echo`` function will be used to show how the PVX API interprets requests. This function has no real interest in itself other than for testing purposes. As its name suggests, it simply echoes back the passed arguments. It will be used to show some of the available input formats. .. _http_api: Using HTTP ---------- Accessing the PVX API through HTTP should be straightforward with common HTTP clients. All the API functions can be called with either the ``GET`` or ``POST`` HTTP methods. The function arguments are usually passed through the query string. The simplest API function invocation over HTTP is to specify its name followed by the arguments. .. https://tools.ietf.org/html/rfc3986#section-3.3 The arguments and their values are delimited by ``=``. The values are interpreted as JSON. .. code-block:: text https:///api/function?argument1=value1&argument2=value2&... To use the streaming mode (see :ref:`API Streaming`), use the ``stream/`` prefix before the function's name. .. code-block:: text https:///api/stream/function?argument1=value1&argument2=value2&... Querying with Curl ^^^^^^^^^^^^^^^^^^ `curl `_ is a well-known tool to access different kind of URLs. It should be available on most UNIX-like systems. The following commands are executed in a POSIX shell. .. code-block:: text $ curl 'https:///api/echo?an_integer=42&a_string="the%20answer"&an_array=\["to%20life","the%20universe","and%20everything"\]' { "type": "result", "result": { "an_integer": 42 "a_string": "the answer", "an_array": [ "to life", "the universe", "and everything" ] } } Notice how the arguments must be escaped to both satisfy ``curl`` and the `URL encoding scheme `_. Alternative ways to make a similar invocation include: .. code-block:: bash curl --get 'https:///api/echo' --data-urlencode 'an_integer=42' --data-urlencode 'a_string="the answer"' --data-urlencode 'an_array=["to life", "the universe", "and everything"]' .. code-block:: bash curl -X POST 'https:///api/echo' --data-urlencode 'an_integer=42' --data-urlencode 'a_string="the answer"' --data-urlencode 'an_array=["to life", "the universe", "and everything"]' .. code-block:: bash curl -X POST ' https:///api/echo' --form-string 'an_integer=42' --form-string 'a_string="the answer"' --form-string 'an_array=["to life", "the universe", "and everything"]' .. code-block:: bash curl -H 'Content-Type: application/json' https:///api/echo --data-raw '{"an_integer": 42, "a_string": "the answer", "an_array": ["to life", "the universe", "and everything"]}' Querying with Invoke-WebRequest ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Invoke-WebRequest `_ is PowerShell cmdlet used to retrieve HTTP pages. The following commands are executed in PowerShell. .. warning:: The ``curl`` command may be automatically aliased to ``Invoke-WebRequest`` and is **not** compatible with the ``curl`` presented above. .. code-block:: bash Get-Alias curl | Select-Object -Property DisplayName DisplayName ----------- curl -> Invoke-WebRequest .. code-block:: bash Invoke-WebRequest 'https:///api/echo?an_integer=42&a_string="the%20answer"&an_array=["to%20life","the%20universe","and%20everything"]' StatusCode : 200 StatusDescription : OK Content : { "result": { "an_array": [ "to life", "the universe", "and everything" ], "a_string": "the answer", "an_integer": 42 }, "... RawContent : HTTP/1.1 200 OK # result truncated for brevity Easier ways to make a similar invocation include: .. code-block:: bash Invoke-WebRequest -Method Post 'https:///api/echo' -Form @{an_integer=42; a_string="the answer"; an_array='["to life", "the universe", "and everything"]'} You could also interpret the JSON content directly, using ``Invoke-RestMethod`` (but this may not work with streaming methods): .. code-block:: bash $(Invoke-RestMethod -Method Post 'https:///api/echo' -Form @{an_integer=42; a_string="the answer"; an_array='["to life", "the universe", "and everything"]'}).result an_array a_string an_integer -------- -------- ---------- {to life, the universe, and everything} the answer 42 .. _ws_api: Using WebSocket --------------- PVX's WebSocket API is largely similar to the HTTP API but the query format has to be adapted to the transport. It is mostly destined to a programmatic usage but the following examples will show a basic utilisation from the CLI. All communication is done via JSON messages. A call must be in the following format: .. code-block:: text { "id": "", "": "", "args": {"": , "": , ...} } Where ``call_type`` is either ``call`` for standard calls or ``stream`` for streaming calls. The ``id`` attribute must be an unique value for each call. For example, you can use a number that is increased at each call, or an UUID. This ``id`` can be used to cancel an unfinished call. Wscat ^^^^^ `wscat `_ is a small WebSocket client. .. code-block:: text wscat -c 'wss:///api/ws' connected (press CTRL+C to quit) > {"id": "example1", "call": "echo", "args": {"an_integer": 42, "a_string": "the answer", "an_array": ["to life", "the universe", "and everything"]}} < { "id": "example1", "type": "result", "result": { "an_integer": 42, "a_string": "the answer", "an_array": [ "to life", "the universe", "and everything" ] } } API Versioning ============== .. we follow https://semver.org, without the PATCH component API URLs should indicate a desired version. This is recommended to ensure that programs using the API will not break when upgrading. For example: .. code-block:: text https:///api/0.1/ https:///api/0.1/doc wss:///api/0.1/ws If a version is not specified, the API will choose the latest version available. When upgrading PVX, the meaning of the API version number is as follow: - an unchanged version means the API is still the same or only received maintenance patches and is fully compatible with the previous one, - an increment of ``MINOR`` (e.g. API 1.2 → API 1.3) means the API only added functionalities and is compatible with the previous one (e.g. a new function or optional arguments have been added), - an increment of ``MAJOR`` (e.g. API 1.3 → API 2.0) means the API introduced breaking changes and is most likely **incompatible** with the previous one (e.g. a function has been removed or may require different arguments). .. _streaming: API Streaming ============= .. TODO: on devrait probablement mieux expliquer ce qu'est le mode stream (qui est sûrement réservé aux dev') In addition to simple calls, the API provides a method to get a stream of results. Each partial result is returned with a message of type ``chunk``, and terminated by a message of type ``end``. When an error is encountered, the stream is interrupted with a message of type ``error``. When using HTTP, add ``stream/`` before the function name in the URL. For example: .. code-block:: text https:///api/1.0/stream/query Example of messages received: .. code-block:: text { "type": "chunk", "chunk": } { "type": "chunk", "chunk": } { "type": "end", } The exact content of the chunks are described by the documentation of each function. A chunk can contains a combination of: - new values (to be added to the already accumulated ones), - updated values (that replace previously given values), - informative values (that are specific to the chunk itself). .. Note:: Every function can stream its results. Likewise, every functions can be queried without streaming. In this case, if the function is producing several values, they are all accumulated and send all at once when the function terminates. API Usage Overview ================== .. _api_keys: API Keys -------- API keys are the preferred way to programmatically authenticate to the API. .. Note:: The current GUI (PVX 5.0) doesn't provide a way to create an API key. You need to use the API itself to create more keys. In a future release, it will be possible to manage the API keys directly from the GUI. .. Warning:: An API key provides a full access to the API. Future enhancement will allow greater granularity on their authorized utilisation. Creation of the First API Key ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creating an API key can be done in two steps: - creating a session from an user account - creating an API key from this session. .. Warning:: This method is temporary, and will be replaced by an easier one in a future version. Creating the session is done with the ``login`` function that expects a ``user`` and ``password`` argument. In this example we will use the default user ``admin`` with the password ``admin``. (In a standard installation, this account has a different password.) The resulting HTTP URL is: .. code-block:: text https:///api/login?user=admin&password=admin Querying it will return something similar to: .. code-block:: text { "type": "result", "result": "session:907c70c2-a78d-43ec-b577-a26b11ab586c" } Here, ``result`` contains our session ID, a reusable temporary token granting access to the API for a certain amount of time. If something goes wrong, a response of type ``error`` will be returned instead. Now that we have a session, we can create an API key. This is done with the ``create-api-key`` function. For that, we will need to pass the session ID in the special ``_session`` argument. .. Note:: It is perfectly fine to use a session ID to use the API, but this is mainly for user interfaces that need to be based on login/password, with a notion of short-lived sessions. So this is not appropriate for program that use the API. Querying the following URL: .. code-block:: text https:///api/create-api-key?name=example&_session=session:907c70c2-a78d-43ec-b577-a26b11ab586c will generate an API key named ``example`` and return it in the ``result``: .. code-block:: text { "type": "result", "result": "secret:ce8e68ec-9048-4c71-87ef-1b7a96dd7567" } The result, ``secret:ce8e68ec-9048-4c71-87ef-1b7a96dd7567``, is your API key and must be kept secret. Using an API Key ^^^^^^^^^^^^^^^^ Once you have an API key, it is usable permanently (until you revoke it) for authenticated calls by passing it as the argument ``_key``. .. TODO: lien vers la documentation PVQL For example, to perform a PVQL query such as ``traffic`` (which is the total traffic that occurred on the last hour by default), combined with the API key, we get: .. code-block:: text https:///api/query?expr=traffic&_key=secret:ce8e68ec-9048-4c71-87ef-1b7a96dd7567 This will return a result containing a list of objects, including one with a ``data`` entry, that will look as follow: .. code-block:: json { "data": [[[], [136308984336]]], "meta": { "accumulated_time": 0.16814637184143066, "elapsed": 0.16814422607421875, "sorted": true } } See ``query`` for an explanation of the format used. If the authentication is invalid (because the credentials are revoked or expired), the result would have been something like this: .. code-block:: bash { "type": "error", "error": "API-AUTHENTICATION-NEEDED", "path": "query" } Authentication Methods ---------------------- The session ID and API key can be provided in different way. The following authentication tokens can be used in HTTP requests: - session IDs: * in a cookie, as "``PVX-Session=``", * in the query string, as "``_session=``". - API keys: * in the headers of the request, as "``PVX-Authorization: ``", * in the query string, as "``_key=``". .. _security-consideration: Security Considerations ======================= The PVX authentication mechanism prevents anybody from getting PVX access without a secret (account credentials, session ID or API key) shared between the client (anyone requesting the PVX API, including the related PVX graphical interface) and the server. **However**, the current authentication mechanism needs this secret to be embedded **in clear-text** in every authentication-needing requests, and the uniqueness of each request is not verified. This means that, if the communications between the client and the server are not properly encrypted, any eavesdropper can acquire this secret, forge valid requests and exploit the privileges it grants. This is why PVX server force the use of TLS (aka SSL). To ensure optimal security, company-specific TLS certificates should be deployed on the server and used by the client to ensure the communication are encrypted and the server identity is verified. At your own risk, on a testing installation, you could circumvent certificate verification, but it is mostly system or tool dependent. For example, with `curl` this is done with the ``--insecure`` option: .. code-block:: bash curl --insecure 'https:///api/...' And with `Invoke-WebRequest` this is done with the ``-SkipCertificateCheck`` option: .. code-block:: bash Invoke-WebRequest -SkipCertificateCheck 'https:///api/...'