Data Feeds provide a powerful way to store structured data, similar to a spreadsheet. This data can then be used in apps and compositions to add flexibility to your content.
The following is a comprehensive guide into all the features available through the API with samples along the way to help you build feature-rich integrations.
The API was designed to take advantage of all Data Feed capabilities, enabling you to dynamically
create and update rows or columns through the updateDataFeed mutation.
The updateDataFeed mutation streamlines the processes
of inserting, modifying, deleting rows and columns in a single API call via its UpdateDataFeedInput.
To execute this mutation, you need to first make a Query type call to the API in order to retrieve the Data Feed ID,
as well as the IDs of the rows and columns you intend to update or delete.
In order to specify the action you wish to take, you can use the rows or columns field,
which can accept any of the objects provided by the UpdateDataFeedRowOps or UpdateDataFeedColumnOps object.
These objects encompass actions such as create, update or delete. Additionally, there are actions
like accept and reject which are used to either approve or reject pending row changes.
You can also create new Data Feeds using the createDataFeed mutation.
This guide contains an example of creating Data Feeds below.
The Data Feed columns and rows can be queried using the dataFeed or dataFeeds connection on the Organization entity.
To filter columns and rows as well as to know more about their fields, see the Data Feed connection.
{
organization {
dataFeeds(first: 1) {
nodes {
id
name
columns(first: 100) {
nodes {
id
kind
name
}
}
rows(first: 5000) {
nodes {
id
values
}
}
}
}
}
}
Running the above query will return data in the following format. Please mind that columns that contains media files, such as images or videos, return a special object containg information about the media. Please refer to the handling media files section of this guide for more information.
{
"data": {
"organization": {
"dataFeeds": {
"nodes": [
{
"id": "1qU8rGm",
"columns": {
"nodes": [
{
"id": "col3psX",
"kind": "TEXT",
"name": "First Column"
},
{
"id": "coljAsE",
"kind": "IMAGE",
"name": "Second Column"
}
]
},
"rows": {
"nodes": [
{
"id": "lJFv",
"values": {
"col3psX": "First Row",
"coljAsE": {
"mediaRef": "3e9b5212dfd.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA",
"kind": "IMAGE",
"name": "first-image.png",
"width": 1280,
"height": 720,
"uploadedAt": "2023-11-02T12:38:59Z"
}
}
},
{
"id": "4lFp8Da",
"values": {
"col3psX": "Second Row",
"coljAsE": {
"mediaRef": "5c21e9aa899.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA",
"kind": "IMAGE",
"name": "second-image.png",
"width": 2161,
"height": 1350,
"uploadedAt": "2023-11-02T18:52:28Z"
}
}
},
{
"id": "7qFxoAQ",
"values": {
"col3psX": "Third Row",
"coljAsE": {
"mediaRef": "bb35fa47c21.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA",
"kind": "IMAGE",
"name": "third-image.png",
"width": 667,
"height": 569,
"uploadedAt": "2023-11-02T20:55:59Z"
}
}
}
]
}
}
]
}
}
}
}
Through the createDataFeed mutation, you can create a new Data Feed with columns and rows.
The mutation accepts the following arguments:
| Name | Type | Description |
name |
string |
Required. The name of the Data Feed. |
parentId |
string |
Optional. Folder ID where the Data Feed will be created. If not provided, the Data Feed will be created on the Organization root folder. |
tags |
string[] |
Optional.
The tags of the Data Feed, e.g. ["A", "B"]
|
columns |
ColumnOpCreate[] |
Optional. Inital columns for the Data Feed. See ColumnOpCreate. |
rows |
RowOpInsert[] |
Optional. Inital rows for the Data Feed. See RowOpInsert. |
The columns and rows fields are optional, but if you want to create a Data Feed with initial
columns and rows, see columns ColumnOpCreate section,
and for rows see RowOpInsert section.
values field when creating the rows.
Create Data Feed with initial columns and rows
mutation {
createDataFeed(input: {
name: "Shine Data Feed"
tags: ["A", "B"]
columns: [
{
# We declare a temporary column ID to reference when inserting rows.
# Temporary IDs must start with "new", be alphanumeric and
# unique for this the mutation.
id: "new0"
name: "NAME"
required: false
kind: TEXT
}
{
id: "new1"
name: "AGE"
required: false
kind: INTEGER
}
]
rows: [
{
# Here we can use the temporary column IDs to insert rows.
values: {
new0: "NICE NAME HERE"
new1: 23
}
}
{
values: {
new0: "ANOTHER NAME HERE"
}
}
]
}) {
dataFeed {
name
id
columns(first: 2) {
nodes {
# Here you will get the final Column ID
id
name
}
}
rows(first: 2) {
nodes {
id
# Values here will reference the final Column ID
values
}
}
}
}
}
To initiate column creation, utilize the create action field within the mutation's columns input.
The create input object need an id and values.
The id is used to identify the column when creating or updating rows. As the column has not yet been created,
it does not have an ID created by the server yet, so you must provide a temporary ID to be used throughout the operation.
id you provide is not already in use. All existing column ids of a Data Feed
start with the prefix col. When providing a temporary id do not use this prefix.
We recommend using temporary IDs that start with new as a prefix like so: new0, new1, new2....
In values field, you currently only need to provide the necessary values, these are name|kind|required,
see more on ColumnOpCreate section.
Create multiples columns with one query
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
columns: {
create: [
{
id: "new0"
name: "NAME"
required: false
kind: TEXT
}
{
id: "new1"
name: "AGE"
required: false
kind: NUMBER
}
]
}
}) {
dataFeed {
id
name
}
}
}
To update a column, provide the column ID and the new values. Use the update action field within the column input field of the mutation.
Each operation in the update array should include the id and values fields.
Update Data Feed columns
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
columns: {
update: [
{
id: "collBs4v"
name: "New Name"
note: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
}
]
}
}) {
dataFeed {
id
name
}
}
}
To delete a column, you only need to provide the column ID. Within the mutation's column input field, use the delete action. Format the
values as JSON key/value pairs in the following manner: { id: columnId }. Ensure that the columnId corresponds to the ID of an existing
column; otherwise, the API will return an error.
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
columns: {
delete: [
{
id: "collBs4v"
}
{
id: "coljAs4q"
}
]
}
}) {
dataFeed {
id
name
}
}
}
To insert a row only need to provide the required values. Utilize the insert action
field within the mutation's rows input.
The row insert object have the values and pending attributes. When inserting a row,
the default value for pending is false. Rows added with pending: true will be marked as pending,
and will require an manual approval, or approval through the API using the approveDataFeedRows mutation or performing an approve action on the row.
The values field is a JSON key/value pairs must follow this format: { columnId: value }.
Ensure that columnId corresponds to an existing column ID, as the API will return an error otherwise.
Rows are appended to the Data Feed if you don't provide an index value. If you provide an index then the row will be inserted
at the given index. Data Feed indexes are 1-based, not 0-based. If the Data Feed already has a row at the given index it will be moved down.
values JSON field.
Create multiple rows using a single mutation
mutation {
updateDataFeed(input: {
dataFeedId: "ppU0MgeN"
rows: {
insert: [
{
# This will be appended to the Data Feed.
pending: true
values: {
colOEs4M: "TEXT"
colXysMD: 9
}
}
{
# This will be inserted at the first index of the Data Feed.
index: 1,
values: {
colOEs4M: "OTHER TEXT"
colXysMD: 2
}
}
]
}
}) {
dataFeed {
id
name
}
}
}
If you only need to append rows there is a simplified mutation that can help you:
mutation {
appendDataFeedRows(input: {
clientMutationId: "random-mutation-id"
dataFeedId: "ppU0MgeN"
pending: true
values: [
{
colOEs4M: "TEXT"
colXysMD: 9
}
]
}) {
clientMutationId
}
}
To modify a row, you must supply both the row ID and the new values for the columns. Use the update action field within the row
input field of the mutation. Each operation within the update array should include the id, values and version fields,
pending is optional and defaults to false. The version field is optional and it is used to ensure that the row has not been modified since it was retrieved,
if this field is omitted the API will not do any check and will overwrite the row with the new values. See more in Handling Conflicts.
You don't need to update all columns. You can choose to update only a single cell by sending only the corresponding columnId in the values field.
If you wish to clear a column you must send its value as null.
You must provide the ID of an existing row, and values field must provide the column ID and the new value is a JSON, ensure the value type is same as the column kind.
ExamplesUpdate multiple rows with different values for each one
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
update: [
{
# Updating the whole row
id: "OAFQa"
version: 1698423308
values: {
colOJn5F: ["TAG"]
colOEs4M: "TEXT"
colXysMD: 9
}
}
{
# Updating only a single cell
id: "VEFe0"
values: {
colOJn5F: ["TAG 1", "TAG 2"]
}
}
{
# Clearing the values of two cells in this row
id: "VEFe0"
values: {
colOJn5F: null
colXysMD: null
}
}
]
}
}) {
dataFeed {
id
name
}
}
}
Update multiple rows to same values using our simplified mutation
mutation {
updateDataFeedRows(input: {
clientMutationId: "random-mutation-id"
dataFeedId: "deU4o8vQ"
rows: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
pending: true
values: {
colOEs4M: "TEXT"
colXysMD: 9
}
}) {
clientMutationId
}
}
For deleting a row, you currently only need the row ID. Utilize the delete operation field within the row
input field of the mutation. Each operation within the delete array should include the id. The pending and version fields,
are optional and defaults to false and null respectively. The version field is used to ensure that the row has not been modified since it was retrieved,
if the this field is omitted the API will not do any check and will overwrite any row operation, see more in Handling Conflicts.
Ensure that id corresponds to an existing row ID, as the API will return an error otherwise.
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
delete: [
{
id: "OAFQa"
pending: true
}
{
id: "VEFe0"
version: 1698423308
}
]
}
}) {
dataFeed {
id
name
}
}
}
or using our simplified mutation:
mutation {
removeDataFeedRows(input: {
clientMutationId: "random-mutation-id"
dataFeedId: "deU4o8vQ"
pending: true
rows: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
}) {
clientMutationId
}
}
On all Data Feed operations you can set the pending attribute to true to create a pending change.
Default value for pending is false. Rows inserted or updated with pending set to true will be marked as pending,
and will require an approval before being available to apps and compositions.
Each rows in a Data Feed can have differents status. The status can be P_INSERT, P_UPDATE, P_DELETE or APPROVED.
Check DataFeedRowStatus to know more about each status.
When inserting, updating or deleting a row, you can set the pending attribute to true to mark the change as pending approval.
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
insert: [
{
# This will insert a row with status = P_INSERT
pending: true
values: {
colOEs4M: "TEXT"
colXysMD: 9
}
}
]
update: [
{
id: "OAFQa"
# This will insert a row with status = P_UPDATE
pending: true
version: 1698423308
values: {
colOEs4M: "TEXT"
}
}
]
delete: [
{
id: "VEFe0"
# Pending set to true will set the status of this row to P_DELETE
# and it will be deleted when the change is approved.
pending: true
}
]
}
}) {
dataFeed {
id
name
}
}
}
Rows with status P_INSERT, P_UPDATE or P_DELETE can be approved
using the updateDataFeed or approveDataFeedRows mutation.
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
approve: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
}
}) {
dataFeed {
name
}
}
}
or using the simplified mutation
mutation {
approveDataFeedRows(input: {
dataFeedId: "deU4o8vQ"
rows: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
}) {
dataFeed {
id
name
}
}
}
Rows with any status except APPROVED can be rejected
using the updateDataFeed or rejectDataFeedRows mutation.
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
reject: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
}
}) {
dataFeed {
name
}
}
}
or using the simplified mutation:
mutation {
rejectRowsDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: [
{
id: "OAFQa"
version: 1698423308
}
{
id: "VEFe0"
}
]
}) {
dataFeed {
id
name
}
}
}
You can mix column and row creation in a single mutation. With all the knowledge above,
you can mix all operations learned from updateDataFeed mutation and make all changes using just one request.
mutation {
updateDataFeed(input: {
dataFeedId: "ppU0MgeN"
columns: {
create: [
{
id: "new0"
name: "NAME"
required: false
kind: TEXT
}
{
id: "new1"
name: "AGE"
required: true
kind: NUMBER
}
]
}
rows: {
update: [
{
id: "hjKT4s"
version: 1698423308
values: {
new1: 45
colXysMD: "THIS COLUMN ALREADY EXISTS"
}
}
]
insert: [
{
pending: true
values: {
new0: "TEXT"
new1: 9
}
}
{
values: {
new0: "SOME TEXT"
new1: 2
colXysMD: "THIS COLUMN ALREADY EXISTS"
}
}
]
}
}) {
dataFeed {
id
name
}
}
}
Whenever a Data Feed row is inserted or updated, it inherits its version from the current Data Feed (the Data Feed version also changes when something is updated). This version is used to carry out checks when the Data Feed is being updated, more precisely to avoid situations where when you update a piece of data and this data has already changed since the last time you collected the information.
So every time you update the values of a row, delete a row, approve or reject changes waiting for approval, you can provide
the row's version field. This field is optional, if omitted, the version check will be ignored and will
overwrite whatever is in the Data Feed, regardless of its row status or version.
If you provide the version field, the API will check if the version you provided matches the current version of the row. If the versions do not match, the API will return an error and fails the mutation, and you will need to retrieve the Data Feed rows again to get the latest version of the row and try again. Or remove the version field from the mutation to ignore the version check.
version can be obtained by querying the Data Feed rows, see DataFeedRow section.
See UpdateDataFeedRowOps to know where to provide the version field.
You can provide the version field in the update, delete, approve and reject operations.
insert operation does not have a version field, because it will always insert a new row, so there is no risk of overwriting data.
Don't forget to see the examples of mutations in the sections above, they show situations with the version being provided.
Columns with kind such as IMAGE, VIDEO or MEDIA return an object
containing information about the media they hold. MEDIA columns can contain any kind of media,
so the returned value also has a kind field to allow you to know whether the uploaded file was
an image or a video.
| Key | Type | Description |
mediaRef |
string |
Contains a value that can be used to update other rows to the same media file, regardless whether it is
in the same data feed or a different one. The mediaRef is valid for use within 7 days.
This value should not be stored and cannot be used for comparison because it changes every 7 days.
|
kind |
string |
The kind of this media. One of "IMAGE" or "VIDEO"
|
name |
string |
The name of the file being uploaded and displayed in the Data Feed.
Like "media.png" or "video.mp4".
|
width |
integer |
The width of this media, in pixels. |
height |
integer |
The height of this media, in pixels. |
duration |
float |
Optional.
If kind is "VIDEO" then this will be duration of the video
in seconds.
|
uploadedAt |
string |
The UTC date and time this media was uploaded to the Data Feed, in ISO 8601 format.
Like this: "2023-11-02T12:38:59Z".
|
Here is how Data Feed media is returned from a query:
{
"data": {
"organization": {
"dataFeed": {
"rows": {
"nodes": [
{
"values": {
"col3psX": "First Row",
"coljAsE": {
"mediaRef": "3e9b5212dfd.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA",
"kind": "IMAGE",
"name": "image-file.png",
"width": 1280,
"height": 720,
"uploadedAt": "2023-11-020T12:38:59Z"
},
"colnYs7Dv": {
"mediaRef": "1e72659edf6.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA",
"kind": "VIDEO",
"name": "video-file.mp4",
"width": 624,
"height": 352,
"uploadedAt": "2023-11-02T16:19:50Z",
"duration": 30.030022
}
}
}
]
}
}
}
}
}
Each media cell contains a mediaRef value that can be used to download the file through HTTP or
use the same file to populate other cells. The mediaRef is valid for use within 7 days.
To download a media file all you have to do is add the mediaRef to this URL:
https://api.onsign.tv/df-media/
Like this:
$ curl https://api.onsign.tv/df-media/1e72659edf6.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA
No authentication is required to download because the mediaRef already contains an authentication token
that allow access to the media for 7 days.
Here is how you can use a mediaRef for updating or inserting rows in a Data Feed:
mutation {
updateDataFeed(input: {
dataFeedId: "deU4o8vQ"
rows: {
update: [
{
id: "OAFQa"
# You can use the mediaRef directly in the column.
values: {
colZGs06: "3e9b5212dfd.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA"
}
}
],
insert: [
{
# Or the mediaRef can be wrapped in an object.
values: {
colZGs06: {
mediaRef: "3e9b5212dfd.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA"
}
}
}
]
}
}) {
dataFeed {
id
name
}
}
}
Using a new image, video or media content in your a Data Feed is two-step process.
First you upload it through a HTTP POST request to the following endpoint:
https://api.onsign.tv/upload-df-media
content:write permission
in the Authorization header, using the value Token <api_token>. Also, remember to include
the Accept: application/json header.
The upload endpoint accepts the following GET parameters:
| Name | Type | Description |
name |
string |
Optional.
The name of the file being uploaded and displayed in the Data Feed.
If not provided, the name will be set dinamically based on the file type.
E.g. media.png, video.mp4.
|
$ curl -X POST -H "Accept: application/json" \
-H "Authorization: token YOUR-TOKEN" \
--data-binary "@file.png" \
https://api.onsign.tv/upload-df-media?name=media.png
Sample response (JSON):
{
"mediaRef": "3e9b5212dfd.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTg4ODMyMDAsInN1YiI6IjFxVThyR20ifQ.ZatNrmka9ldxJ6ayaA0aThUEhfiiNi4k-5TasmDESEA"
}
After calling the Data Feed upload endpoint, you'll receive a mediaRef which will can used when updating media of a Data Feed cell.
Please mind that the returned mediaRef is temporary and can be used for up to 7 days.
After using it on a mutation the value retrieved from the Data Feed cell will be different.