--- title: "Kernel Tasks SDK" slug: "kernel-tasks-sdk" updated: 2025-10-21T17:56:18Z published: 2025-10-21T17:56:18Z canonical: "docs.kernel.com/kernel-tasks-sdk" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.kernel.com/llms.txt > Use this file to discover all available pages before exploring further. # Kernel Tasks SDK The Kernel SDK allows you to emit your own task events to be recorded alongside Flow data in the same dataset. #### Specification To send events to our system, send 2 TCP packets for each event to the computer being used to acquire Flow data on port **6767**. The first packet should contain the byte size of the JSON payload represented as a network-endian/big-endian unsigned 4 byte integer. The second packet should contain the JSON bytes itself. `The fields in the JSON should be:` | **NAME** | **TYPE** | **NOTES** | | --- | --- | --- | | id | integer | An incrementing integer signifying a unique event identifier | | timestamp | integer | An epoch timestamp in microseconds | | event | string | The name of the event | | value | string | The value of the event | If tasks are not running on the computer being used to acquire Flow data, ensure that: - The TCP packets can reach the data acquisition computer over the network - The two computers' clocks are synchronized via NTP or a similar protocol ### Conventions #### Context Setting Events To take advantage of the export and analysis tools available in the Kernel Researcher Portal, structure your custom tasks according to the following conventions: - The first event must be **start_experiment.** - The last event must be **end_experiment**. - A task must be structured using the following hierarchy of events: *experiment > task > block > trial.* - *Task*, *block*, and *trial* are optional, but the hierarchy must be respected. (e.g. a *block* cannot be within a *trial*.) - The value for these context events MUST be an integer that represents the ordinal index (block 1, block 2, ..., block N) - *Epochs* (events with a non-zero duration) must be delineated by 2 events: - **start_{}** - **end_{}** - You can additionally describe a context with a **{}_type** event where the value is the description. - A duration will be calculated from these and will appear in the task data exported in the SNIRF files. #### Instantaneous Events *Events* (with a zero duration) which you wish to record a timestamp for MUST start with **event_.** #### **Metadata Events** All other events (those which are not prefixed with **start_, end_,** or **event_** are considered metadata and their timestamps are not included in the task data in exported SNIRF files. #### Additional Notes The *value* field of an event can be a dictionary, in which case its keys appear as separate columns (prefixed with the name of the event) in the task data in exported SNIRF files. BEST PRACTICES REMINDERSend events whose timestamps matter for your analyses as close in time as possible to when events actually happen in the physical world. E.g., for visual stimuli displayed on a screen, events should be sent right after the screen flip command in environments such as psychopy (Python) or Psychtoolbox (Matlab). #### Data Example A simple finger tapping paradigm may create this sequence of events: ```json {"id": 1, "timestamp": 1709500189972160, "event": "start_experiment", "value": "1"} {"id": 2, "timestamp": 1709500189972160, "event": "experiment_type", "value": "finger_tapping"} {"id": 3, "timestamp": 1709500189972160, "event": "start_rest", "value": "1"} {"id": 4, "timestamp": 1709500189972169, "event": "end_rest", "value": "1"} {"id": 5, "timestamp": 1709500189972169, "event": "start_block", "value": "1"} {"id": 6, "timestamp": 1709500189972169, "event": "block_type", "value": "right"} {"id": 7, "timestamp": 1709500189972175, "event": "end_block", "value": "1"} {"id": 8, "timestamp": 1709500189972175, "event": "start_rest", "value": "2"} {"id": 9, "timestamp": 1709500189972180, "event": "end_rest", "value": "2"} {"id": 10, "timestamp": 1709500189972180, "event": "start_block", "value": "2"} {"id": 11, "timestamp": 1709500189972180, "event": "block_type", "value": "left"} {"id": 12, "timestamp": 1709500189972185, "event": "end_block", "value": "2"} ... {"id": 248, "timestamp": 1709500246184622, "event": "end_experiment", "value": "1"} ``` This series of events would be transformed by the Kernel Analysis tools to the following flattened version, ready for analysis (and export) | timestamp | event | duration | experiment | experiment_type | rest | block | block_type | | --- | --- | --- | --- | --- | --- | --- | --- | | 0.000287 | start_experiment | 1129.979274 | 1.0 | finger_tapping | NaN | NaN | nan | | 0.016940 | start_rest | 23.723486 | 1.0 | finger_tapping | 1 | NaN | nan | | 23.740575 | start_block | 5.051849 | 1.0 | finger_tapping | NaN | 1.0 | right | | 28.812785 | start_rest | 20.218486 | 1.0 | finger_tapping | 2 | NaN | nan | | 49.031372 | start_block | 5.032070 | 1.0 | finger_tapping | NaN | 2.0 | left | Note that the timestamps are "zeroed" to the beginning of the first timestamp of the fNIRS data. #### Task Events SDK Client Kernel provides a Task SDK Client to make it easier to send events in the format described. See [Kernel SDK](/v1/docs/kernel-sdk-install) for available languages and instructions. #### Code Examples ```python import json import socket import struct from time import time # one time connection host = 'acquisition.computer.host' port = 6767 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) # then send events id=1 timestamp = time() data_to_send = { "id": id, "timestamp": int(timestamp * 1e6), "event": "start_trial", "value": "5", } event = json.dumps(data_to_send).encode("utf-8") msg = struct.pack("!I", len(event)) + event sock.sendall(msg) ``` ```csharp using System; using System.Net; using System.Net.Sockets; using Newtonsoft.Json; using System.Text; class KernelEvent { public int id { get; set; } public long timestamp { get; set; } public string @event { get; set; } public string value { get; set; } } class KernelEvents { const int PORT = 6767; NetworkStream nwStream; int id = 0; DateTime epoch = new DateTime(1970, 1, 1); public KernelEvents(string ip) { TcpClient client = new TcpClient(ip, PORT); this.nwStream = client.GetStream(); } public void send(KernelEvent kernelEvent) { this.id++; kernelEvent.id = this.id; TimeSpan timestamp = DateTime.UtcNow - epoch; kernelEvent.timestamp = (long)(timestamp.TotalMilliseconds * 1000); string json = JsonConvert.SerializeObject(kernelEvent); Console.WriteLine(json); byte[] bytes = Encoding.UTF8.GetBytes(json); byte[] size = BitConverter.GetBytes(bytes.Length); if (BitConverter.IsLittleEndian) Array.Reverse(size); this.nwStream.Write(size, 0, size.Length); this.nwStream.Write(bytes, 0, bytes.Length); } } public class Program { public static void Main(string[] args) { KernelEvents kernelEvents = new KernelEvents("acquisition.computer.ip"); kernelEvents.send(new KernelEvent { @event="start_trial", value="5", }); } } ``` Matlab: [https://www.mathworks.com/help/instrument/write-and-read-data-over-the-tcpip-interface.html](https://www.mathworks.com/help/instrument/write-and-read-data-over-the-tcpip-interface.html)