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.