Skip to main content

Web API

Testspace provides a REST API, designed to have predictable, resource-oriented URLs that leverage built-in features of HTTP, like authentication, verbs and response codes. All data is sent and received in JSON format and UTF-8 encoding. Any off-the-shelf HTTP client can be used to communicate with the API.

This API is not for publishing test results. The Testspace client is required for pushing data to the server.

Overview#

Authentication#

Testspace API is authenticated using HTTP Basic Auth over HTTPS. Any requests over plain HTTP will fail.

The credentials may be given either as username:password or as access-token:(You can generate and get an Access token from your user settings).

For example using curl you can pass the -u flag:

$ curl -u <username:password> "https://domain.testspace.com/api/projects"
$ curl -u <access-token:> "https://domain.testspace.com/api/projects"

or set the Authorization header:

$ curl -H "Authorization: Token <access-token>" "https://domain.testspace.com/api/projects"

All requests are associated with a specific user in Testspace and permissions are limited to that user's capabilities.

Requests#

The base URL of the API is https://domain.testspace.com/api where domain should be replaced with your organization subdomain.

Parameters#

Many API methods take optional parameters. For GET requests, any parameters not specified as a segment in the path can be passed as an HTTP query string parameter:

$ curl -u username:password "https://domain.testspace.com/api/projects/123/spaces?name=ABC"

In this example, the value '123' is provided for the project :id parameter in the path while :name is passed in the query string.

For POST, PATCH, PUT, and DELETE requests, parameters not included in the URL must be encoded as JSON with a Content-Type of application/json, or the API will return a 415 Unsupported Media Type status code:

$ curl -u username:password "https://domain.testspace.com/api/spaces/1234" \
-X PATCH \
-H 'Content-Type: application/json' \
-d '{"description":"some description"}'

HTTP Verbs#

The API strives to use appropriate HTTP verbs for each action:

  • GET - To retrieve a resource or a collection of resources
  • POST - To create a resource
  • PATCH or PUT - To modify a resource
  • DELETE - To delete a resource

Responses#

All response bodies, including errors, are JSON encoded with a Content-Type of application/json.

A single resource is represented as a JSON object:

{
"field1": "value",
"field2": true,
"field3": []
}

A collection of resources is represented as a JSON array of objects:

[
{
"field1": "value",
"field2": true,
"field3": []
},
{
"field1": "another value",
"field2": false,
"field3": []
}
]

All timestamps are returned in ISO 8601 format.

Unset fields will be represented as a null instead of being omitted. If the field is an array, it will be represented as an empty array - i.e. [].

Errors#

The API uses HTTP status codes to indicate success or failure of a request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that resulted from the provided information (e.g. authentication failed, a resource not found, a validation error, etc.), and codes in the 5xx range indicate an internal server error.

All 4xx series errors will be returned with a JSON object in the body:

{
"message": "Not Found"
}

5xx series error codes do not return JSON bodies.

Pagination#

Listing resources will be paginated to 30 items by default. To control that you can pass the following parameters:

parameterdescription
pagePage number (default: 1)
per_pageNumber of items to list per page (default: 30, max: 100)

For example to list 50 projects per page:

$ curl -u username:password "https://domain.testspace.com/api/projects?per_page=50"

A Link header is sent back with each response. It has a rel set to prev/next/first/last and contains the relevant URL. Please use these links instead of constructing your own URLs.

Projects#

All the information in Testspace is organized in Projects. A project contains one or more Spaces.

FieldTypeNotes
idintegerThe unique ID of the project
namestringThe name of the project
descriptionstringThe description of the project
is_privatebooleanTrue if the project is private, false otherwise
source_repo_urlstringThe URL of the repo if connected project
created_attimestampThe date/time when the project was created
updated_attimestampThe date/time when the project was last updated

To request details about projects and to create or modify projects use the following API methods.

List projects#

GET /api/projects

A successful response includes an array of projects, as each entry in the list follows the same format as get a project:

200 OK
Content-Type: application/json
[
{ "id": 123, "name": "..." },
{ "id": 345, "name": "..." },
]

Get a project#

GET /api/projects/:project_id
parameterdescription
:project_idThe name or the ID of a project

A successful response looks like:

200 OK
Content-Type: application/json
{
"id": 123,
"name": "MyProject",
"description": "Some description for my project",
"is_private": true,
"source_repo_url": null,
"created_at": "2014-10-06T18:02:54Z",
"updated_at": "2014-12-04T19:59:42Z"
}

Create a project#

POST /api/projects
FieldRequiredNotes
nameyesThe name of the project
descriptionnoThe description of the project
is_privatenoTrue (default) if the project is private, false otherwise

Sample request:

POST /api/projects
Content-Type: application/json
{
"name": "MyProject",
"description": "Some description for my project",
"...": "..."
}

A successful response returns the new project following the same format as get a project:

201 Created
Location: https://domain.testspace.com/api/projects/123
Content-Type: application/json
{
"id": 123,
"name": "MyProject",
"...": "..."
}

Update a project#

PATCH /api/projects/:project_id
parameterdescription
:project_idThe ID of a project
FieldRequiredNotes
namenoThe name of the project
descriptionnoThe description of the project
is_privatenoTrue if the project is private, false otherwise

Sample request:

PATCH /api/projects/123
Content-Type: application/json
{
"name": "NewProject",
"...": "..."
}

A successful response returns no content:

205 Reset Content

Delete a project#

DELETE /api/projects/:project_id
parameterdescription
:project_idThe ID of a project

Sample request:

DELETE /api/projects/123

A successful response returns no content:

204 No Content

Spaces#

Each Project consists of Spaces. A Space is a collection of test Resultsand Metrics.

FieldTypeNotes
idintegerThe unique ID of the space
namestringThe name of the space
descriptionstringThe description of the space
project_idintegerThe associated project
sandboxbooleanTrue if the space is a sandbox, false otherwise
min_run_periodintegerPublication deadline for results in days (0 to disable)
active_result_set_idintegerThe current active (aka latest-complete) result set
latest_result_set_idintegerThe latest result set
created_attimestampThe date/time when the space was created
updated_attimestampThe date/time when the space was last updated

To request details about spaces and to create or modify spaces use the following API methods.

List spaces#

GET /api/projects/:project_id/spaces
parameterdescription
:project_idThe name or the ID of the associated project

A successful response includes an array of spaces, as each entry in the list follows the same format as Get a space:

200 OK
Content-Type: application/json
[
{ "id": 1234, "name": "..." },
{ "id": 5678, "name": "..." },
]

Get a space#

GET /api/spaces/:space_id
parameterdescription
:space_idThe ID of a space
GET /api/projects/:project_id/spaces/:space_name
parameterdescription
:project_idThe name or the ID of the associated project
:space_nameThe URL-encoded name of a space

A successful response looks like:

200 OK
Content-Type: application/json
{
"id": 1234,
"name": "MySpace",
"description": "Some description for my space",
"project_id": 12,
"sandbox": false,
"min_run_period": 1,
"active_result_set_id": 10823,
"latest_result_set_id": 10823,
"created_at": "2014-10-06T18:02:54Z",
"updated_at": "2014-12-04T19:59:42Z"
}

Create a space#

POST /api/projects/:project_id/spaces
parameterdescription
:project_idThe ID of the associated project
FieldRequiredNotes
nameyesThe name of the space
descriptionnoThe description of the space
sandboxnoTrue for a sandbox space, false (default) otherwise
basenoThe name of an existing Space to copy settings from

Sample request:

POST /api/projects/12/spaces
Content-Type: application/json
{
"name": "MySpace",
"description": "Some description for my space",
"...": "..."
}

A successful response returns the new space following the same format as Get a space:

201 Created
Location: https://domain.testspace.com/api/spaces/1234
Content-Type: application/json
{
"id": 1234,
"name": "MySpace",
"...": "..."
}

Update a space#

PATCH /api/spaces/:space_id
parameterdescription
:space_idThe ID of a space
FieldRequiredNotes
namenoThe name of the space
descriptionnoThe description of the space
sandboxnoTrue for a sandbox space, false otherwise
min_run_periodnoPublication deadline for results in days (0 to disable)

Sample request:

PATCH /api/spaces/1234
Content-Type: application/json
{
"name": "NewSpace",
"...": "..."
}

A successful response returns no content:

205 Reset Content

Delete a space#

DELETE /api/spaces/:space_id
parameterdescription
:space_idThe ID of a space

Sample request:

DELETE /api/spaces/1234

A successful response returns no content:

204 No Content

Results#

Each Space consists of a collection of Results. A Result is a collection of test cases organized in folders and suites.

FieldTypeNotes
idintegerThe unique ID of the result
namestringThe name of the result
descriptionstringThe description of the result
completebooleanTrue if the result is a complete, false otherwise
pinnedbooleanTrue if the result is a pinned, false otherwise
space_idintegerThe associated space
user_idintegerThe user who created the result
commit_idstringThe associated repo commit id (e.g. SHA for git)
build_urlstringThe URL of the originating CI build
suite_countsarrayThe contained suites [passed, failed, na] counters
session_suite_countsarrayThe contained suites [passed, failed, na] counters for session only results
case_countsarrayThe contained cases [passed, failed, na, errored] counters
session_case_countsarrayThe contained cases [passed, failed, na, errored] counters for session only results
annotation_countsarrayThe contained annotations [error, warn, info] counters
failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters
session_failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters for session only results
durationnumberThe duration of the result in milliseconds
session_durationnumberThe duration of the result in milliseconds for session only results
healthobjectThe health of the result
created_attimestampThe date/time when the result was created
updated_attimestampThe date/time when the result was last updated

To request details about results and to create or modify results use the following API methods.

List results#

GET /api/projects/:project_id/spaces/:space_id/results
GET /api/spaces/:space_id/results
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space

A successful response includes an array of results, as each entry in the list follows the same format as Get a result:

200 OK
Content-Type: application/json
[
{ "id": 1234, "name": "..." },
{ "id": 5678, "name": "..." },
]

Get a result#

This API point is provided only for the purpose of accessing a result record without its content. To obtain a result's content please use the Get result content API or if interested in the associated result failures use the Get result failures API.

GET /api/projects/:project_id/spaces/:space_id/results/:result_id
GET /api/spaces/:space_id/results/:result_id
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space
:result_idThe name or the ID of a result

A successful response looks like:

200 OK
Content-Type: application/json
{
"id": 123456,
"name": "MyResult",
"description": "Some description for my result",
"complete": true,
"pinned": false,
"user_id": 15,
"space_id": 1234,
"commit_id": "220cc4abf6989d38726403a085c542290c7f4c13",
"build_url": "https://...",
"suite_counts": [1508, 147, 4],
"session_suite_counts": [1508, 147, 4],
"case_counts": [35093, 517, 292, 5],
"session_case_counts": [35093, 517, 292, 5],
"annotation_counts": [5951, 3558, 332415],
"failure_counts": [5, 17, 495, 21, 8, 35],
"session_failure_counts": [5, 17, 495, 21, 8, 35],
"duration": 7201360.0,
"session_duration": 7201360.0,
"health": {
"state": "failure",
"description": "Unhealthy. One or more Testspace metrics were out of range...",
"badges": [
{
"metric_id": 1234567,
"name": "test",
"value": 35,
"status": "failing"
},
]
},
"created_at": "2014-10-06T18:02:54Z",
"updated_at": "2014-12-04T19:59:42Z"
}

Create a result#

This API point is provided only for the purpose of creation of empty (no content) result records. To create a result and push content to it please use the Testspace client.

POST /api/spaces/:space_id/results
parameterdescription
:space_idThe ID of the associated space
FieldRequiredNotes
nameyesThe name of the result
descriptionnoThe description of the result
commit_idnoThe associated repo commit id
build_urlnoThe URL of the originating CI build

Sample request:

POST /api/spaces/1234/results
Content-Type: application/json
{
"name": "MyResult",
"description": "Some description for my result",
}

A successful response returns the new result following the same format as Get a result:

201 Created
Location: https://domain.testspace.com/api/spaces/1234/results/123456
Content-Type: application/json
{
"id": 123456,
"name": "MyResult",
"...": "..."
}

Update a result#

This API point is provided only for the purpose of updating a result record without affecting its content. To update a result's content please use the Testspace client.

PATCH /api/spaces/:space_id/results/:result_id
parameterdescription
:space_idThe ID of a space
:result_idThe ID of a result
FieldRequiredNotes
namenoThe name of the result
descriptionnoThe description of the result
completenoTrue for a complete result, false otherwise
pinnednoTrue for a pinned result, false otherwise
commit_idnoThe associated repo commit id
build_urlnoThe URL of the originating CI build

Sample request:

PATCH /api/spaces/1234/results/123456
Content-Type: application/json
{
"name": "NewResult",
"...": "..."
}

A successful response returns no content:

205 Reset Content

Delete a result#

DELETE /api/spaces/:space_id/results/:result_id
parameterdescription
:space_idThe ID of a space
:result_idThe ID of a result

Sample request:

DELETE /api/spaces/1234/results/123456

A successful response returns no content:

204 No Content

Get result contents#

Each Result is a collection of test cases organized in folders and suites.

Folder#

FieldTypeNotes
idintegerThe unique ID of the folder
typestringAlways folder
namestringThe name of the folder
pathstringThe full path to the folder
descriptionstringThe description of the folder
carriedbooleanTrue if the folder has one or more suites carried from another session, false otherwise
suite_countsarrayThe contained suites [passed, failed, na] counters
session_suite_countsarrayThe contained suites [passed, failed, na] counters for session only results
case_countsarrayThe contained cases [passed, failed, na, errored] counters of result
session_case_countsarrayThe contained cases [passed, failed, na, errored] counters for session only results
annotation_countsarrayThe contained annotations [error, warn, info] counters
failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters
session_failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters for session only results
durationnumberThe duration of the folder in milliseconds
session_durationnumberThe duration of the folder in milliseconds for session only results

Suite#

FieldTypeNotes
idintegerThe unique ID of the suite
typestringAlways suite
namestringThe name of the suite
pathstringThe full path to the suite
descriptionstringThe description of the suite
statusstringThe status of the suite (i.e. passed, failed, na)
carriedbooleanTrue if the suite is carried from another session, false otherwise
case_countsarrayThe contained cases [passed, failed, na, errored] counters
session_case_countsarrayThe contained cases [passed, failed, na, errored] counters for session only results
annotation_countsarrayThe contained annotations [error, warn, info] counters
failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters
session_failure_countsarrayThe contained failures [new, flaky, consistent, passing, resolved, exempt] counters for session only results
durationnumberThe duration of the suite in milliseconds
session_durationnumberThe duration of the suite in milliseconds for session only results
download_urlstringThe URL to download from an XML-snippet of the contained test cases

Annotation#

FieldTypeNotes
idintegerThe unique ID of the annotation
typestringAlways annotation
namestringThe name of the annotation
descriptionstringThe description of the annotation
download_urlstringThe URL to download from the associated file content

To get the contents of a result use the following API methods.

GET /api/projects/:project_id/spaces/:space_id/results/:result_id/contents/<:path>
GET /api/spaces/:space_id/results/:result_id/contents/<:path>
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space
:result_idThe name or the ID of a result
:pathThe named folder/suite path

A successful response looks like:

  • a collection, when :path represents a Folder
200 OK
Content-Type: application/json
[
{
"id": 1234,
"type": "folder",
"name": "MyFolder",
"path": "path/MyFolder",
"description": "Some description for my folder",
"suite_counts": [150, 14, 4],
"case_counts": [3509, 51, 29, 5],
"annotation_counts": [5951, 355, 3324],
"failure_counts": [5, 17, 49, 21, 8, 35],
"duration": 1234.5
},
{
"id": 5678,
"type": "suite",
"name": "MySuite",
"path": "path/MySuite",
"description": "Some description for my suite",
"status": "failed",
"case_counts": [50, 6, 2, 0],
"annotation_counts": [10, 0, 3],
"failure_counts": [5, 17, 4, 2, 8, 5],
"duration": 1234.5,
"download_url": "https://..."
},
{
"id": 16369873,
"type": "annotation",
"name": "MyAnnotation",
"description": "Some description for my annotation",
"download_url": "https://..."
},
]
  • an object, when :path represents a Suite
200 OK
Content-Type: application/json
{
"id": 5678,
"type": "suite",
"name": "MySuite",
"path": "path/MySuite",
"description": "Some description for my suite",
"status": "failed",
"case_counts": [50, 6, 2, 0],
"annotation_counts": [10, 0, 3],
"failure_counts": [5, 17, 4, 2, 8, 5],
"duration": 1234.5,
"download_url": "https://..."
}

Get result failures#

For each Result any failed/errored test case is being tracked. The set of those test cases is provided via the Failures end point.

FieldTypeNotes
keystringThe key of a test case failure (formatted as 1path/to/suite().case`)
statestringThe state of the failure (i.e. new, flaky, consistent, passing, resolved)
historyarrayThe history of the failure, represented with a first-letter-notation array, where:
-F is for failed,
-P - passed,
-N - non-applicable,
-E - errored,
-M - missing
In case of exemption X is prepended. If the type repeats the number of consecutive times is appended to the letter Fn.
exemptbooleanTrue if the failure is a 'exempt', false otherwise
messagestringThe triage message associated with the failure
tracked_countintegerThe number of results the failure has been tracked for
failed_attimestampThe date/time when the failure first occurred
passed_attimestampThe date/time when the failure started passing before being resolved

To get the failures of a result use the following API methods.

GET /api/projects/:project_id/spaces/:space_id/results/:result_id/failures
GET /api/spaces/:space_id/results/:result_id/failures
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space
:result_idThe name or the ID of a result

A successful response looks like:

200 OK
Content-Type: application/json
[
{
"key": "path/to/MySuite().MyCase",
"state": "flaky",
"history": ["F", "P", "F", "F"],
"exempt": false,
"message": null,
"tracked_count": 4,
"failed_at": "2014-10-06T18:02:54Z",
"passed_at": null
},
]

Metrics#

For each Result push to a Space a set of Metrics are collected. A Metric consists of a collection of historical datasets.

FieldTypeNotes
idintegerThe unique ID of the metric
namestringThe name of the metric
data_sourcestringThe path to the data source of the metric
chart_typestringThe type (i.e. lines or columns) of the metric's chart
max_rangeintegerThe "default" max range on the metric's chart y-axes
max_range_limitintegerThe max range "limit" (0 for unlimited) on the metric's chart y-axes
unitsstringThe units of the data being graphed
space_idintegerThe associated space
badge_urlstringThe URL of the associated badge
variablesarrayThe "variables" (calculated of the input dataset) being graphed
thresholdsarrayThe "thresholds" (calculated of the input dataset) being graphed
badgeobjectThe "badge" (calculated of the variables/thresholds and/or input datasets) being computed
created_attimestampThe date/time when the metric was created
updated_attimestampThe date/time when the metric was last updated

To request details about metrics and to create or modify metrics use the following API methods.

List metrics#

GET /api/projects/:project_id/spaces/:space_id/metrics
GET /api/spaces/:space_id/metrics
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space

A successful response includes an array of metrics, as each entry in the list follows the same format as Get a metric:

200 OK
Content-Type: application/json
[
{ "id": 1234, "name": "..." },
{ "id": 5678, "name": "..." },
]

Get a metric#

This API point is provided only for the purpose of accessing a metric record without its datasets. To obtain metric's datasets please use the Get Metric Datasets API.

GET /api/projects/:project_id/spaces/:space_id/metrics/:metric_id
GET /api/spaces/:space_id/metrics/:metric_id
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space
:metric_idThe ID of a metric

A successful response looks like:

200 OK
Content-Type: application/json
{
"id": 123456,
"name": "MyMetric",
"data_source": "/path/to/suite[dataset_label]",
"chart_type": "lines",
"max_range": 10,
"max_range_limit": 10,
"units": "seconds",
"space_id": 1234,
"badge_url": "https://...",
"variables": [
{
"name": "var-one",
"color": "#008000",
"expression": "round(d1/1000,1)"
},
],
"thresholds": [
{
"name": "thresh-one",
"color": "#464646",
"expression": "3"
},
],
"badge": {
"name": "thresh-one",
"value_expression": "concat(v1,'s')",
"criteria_expression": "v1 \u003c= t1",
"is_enabled": true
},
"created_at": "2014-10-06T18:02:54Z",
"updated_at": "2014-12-04T19:59:42Z"
}

Create a metric#

Only Custom Metrics can be created via this API.

POST /api/spaces/:space_id/metrics
parameterdescription
:space_idThe ID of the associated space
FieldRequiredNotes
nameyesThe name of the metric
data_sourceyesThe path to the data source of the metric
chart_typenoThe type (i.e. lines or columns) of the metric's chart
max_rangenoThe "default" max range on the metric's chart y-axes
unitsnoThe units of the data being graphed
variablesyesThe "variables" (calculated of the input dataset) being graphed
thresholdsnoThe "thresholds" (calculated of the input dataset) being graphed
badgenoThe "badge" (calculated of the variables/thresholds and/or input datasets) being computed

Sample request:

POST /api/spaces/1234/metrics
Content-Type: application/json
{
"name": "MyMetric",
"data_source": "/path/to/suite[dataset_label]",
"...": "..."
}

A successful response returns the new metric following the same format as Get a metric:

201 Created
Location: https://domain.testspace.com/api/spaces/1234/metrics/123456
Content-Type: application/json
{
"id": 123456,
"name": "MyMetric",
"...": "..."
}

Update a metric#

PATCH /api/spaces/:space_id/metrics/:metric_id
parameterdescription
:space_idThe ID of a space
:metric_idThe ID of a metric
FieldRequiredNotes
namenoThe name of the metric
chart_typenoThe type (i.e. lines or columns) of the metric's chart
max_rangenoThe "default" max range on the metric's chart y-axes
unitsnoThe units of the data being graphed
variablesnoThe "variables" (calculated of the input dataset) being graphed
thresholdsnoThe "thresholds" (calculated of the input dataset) being graphed
badgenoThe "badge" (calculated of the variables/thresholds and/or input datasets) being computed

Sample request:

PATCH /api/spaces/1234/metrics/123456
Content-Type: application/json
{
"name": "NewMetric",
}

A successful response returns no content:

205 Reset Content

Delete a metric#

DELETE /api/spaces/:space_id/metrics/:metric_id
parameterdescription
:space_idThe ID of a space
:metric_idThe ID of a metric

Sample request:

DELETE /api/spaces/1234/metrics/123456

A successful response returns no content:

204 No Content

Get metric datasets#

A Metric consists of a collection of historical datasets.

FieldTypeNotes
idintegerThe unique ID of the metric
metric_idintegerThe associated metric
result_set_idintegerThe associated result
raw_valuesobjectThe set of raw metric data (named d1 to d10) pushed with the result
evaluated_valuesobjectThe set of evaluated data (named v1..v5, t1, t2, b and bc) that are being graphed
created_attimestampThe date/time when the metric dataset was created
updated_attimestampThe date/time when the metric dataset was last updated

To get the datasets of a metric use the following API methods.

GET /api/projects/:project_id/spaces/:space_id/metrics/:metric_id/datasets
GET /api/spaces/:space_id/metrics/:metric_id/datasets
parameterdescription
:project_idThe name or the ID of the associated project
:space_idThe name (only when project is specified) or the ID of the associated space
:metric_idThe ID of a metric

A successful response looks like:

200 OK
Content-Type: application/json
[
{
"id": 369040,
"metric_id": 94828,
"result_set_id": 112168,
"raw_values": {"d1":2093,"d2":1900,"d3":2740,"d4":null,"d5":null,"d6":null,"d7":null,"d8":null,"d9":null,"d10":null},
"evaluated_values": {"v1":"2.1","v2":"1.9","v3":"2.7","v4":null,"v5":null,"t1":3,"t2":null,"b":"2.1s","bc":true},
"created_at":"2019-10-11T10:58:02.000-07:00",
"updated_at":"2019-10-11T10:58:02.000-07:00",
"published_at":"2019-10-11T10:58:02.000-07:00"
},
]