Introduction
The Shiny Reviews API allows you to interact with your Shiny Reviews project and attached reviews. Create, search, filter, or fetch, all your reviews.
Base URL:
https://[project_code].shiny.reviews
This documentation aims to provide all the information you need to work with our API.
As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).
Authenticating requests
To authenticate requests, include a query parameter token
in the request.
All authenticated endpoints are marked with a requires authentication
badge in the documentation below.
You can retrieve your token by visiting your dashboard and going to your project settings.
Using tokens
You can create as many tokens as you need.
You can give tokens a name as well as control their "active" status. This lets you rotate tokens safely and only deactivate a token that has been replaced.
There are two types of tokens:
READ Token
This token is required when you are using API endpoints that will read data. For example, listing reviews.
We use a READ token for this so that you do not expose your WRITE token to the open internet. We use these two tokens together so that you can control how data is accessed on your project.
You can create as many tokens as you want.
WRITE Token
This token is required when you are using API endpoints that will store data. For example, storing a review.
We use a WRITE token for this so that you do not expose your reviews endpoint to the open internet unprotected. We don't want back actors filling your project with bunk reviews, spam, or unvalidated data.
We don't want back actors filling your project with bunk reviews, spam, or unvalidated data.
We recommend you the endpoints that require this token on your server. Depending on the tools you are using, you may need to implement custom endpoints so that you can validate, then store, your reviews on the server.
Here are some quick links for a few frontend frameworks:
- Next.js Server Action
- Next.js Route Handler
- Nuxt Server Endpoint
- Svelte.js Form Action
- Solid.js Solid Router Action
If you are using a backend framework (Rails, Django, Laravel, etc.), I will assume you are familiar with how to call external APIs on the server in your given framework of choice.
Features
There are a variety of features in your project that can be turned on or off. This can help reduce the complexity of your usage of the API or just add some piece of mind that you aren't enabling something you don't even need.
Ratings style
Ratings style controls the type of validation that will be used when storing a rating.
The choices are:
- Stars (Amazon style): between 1 and 5
- Thumbs (YouTube style): either 0 or 1
- Liked (Netflix style): 0, 50, or 100
- Percent: between 1 and 100
If you have some more custom details to store on your review, you an use Metafields.
Review approval
Only reviews you have manually approved will be shown in the API.
Review notification
Notify the admin when a new review is created. This could be important if "review approval" is on.
Public/unprotected
The reviews and project details index/list endpoint is public and will not require a READ API token. Reviews can never be created without a WRITE API token.
Domain locked
Disallow requests to the API from the domain that does not match the domain set in your project. Be careful, as this means that you will not be able to easily test your integration on anything other than the domain provided.
For this filter, we check the Referer
header on requests.
Markdown
Markdown parsing is not always perfect. So be sure to check the review content when approving it to make sure it does not have any potential XSS issues.
Parse the body of the reviews as markdown. If you don't know what markdown is, just leave this disabled.
The settings for parsing/generating markdown have "strip HTML" and "disallow unsafe links" turned on. So you can be more confident that the HTML output is more safe to display.
Webhook
Send a GET request to any valid URL once a review has been added, or if review approval is on, after it is approved.
You can use this feature to trigger builds of static sites that need to show the latest reviews or review summaries.
If your webhook URL was set to https://static-hosting.com/webhook-url
, then the request to the endpoint would look something like the following:
curl --request GET \
--url 'https://static-hosting.com/webhook-url?project=PROJECT_CODE&review=REVIEW_ID'
--header 'X-Shiny-Reviews: PROJECT_CODE'
If the request fails, an email will be sent to the project admin about the failed request.
The Shiny Review server will attempt to send a request to the endpoint up to 3 times before giving up.
The retry is setup to delay each by 1000 additional milliseconds between requests.
If the request still fails after the 3rd attempt, an email will be sent to the project admin about the failed request.
Metafields
You can think of meta fields as custom fields. These can be simple or more complicated based on your use case.
Some use cases for metafields are as follows:
- Tagging a review with a specific details
- Storing the specific entity id that the review is attached to
- Filtering a review by it's metafields
- Finding reviews that have one or many values in a metafield
With metafields, we have a powerful mechanism for creating reviews with additional information that is important for later showing, sorting, or filtering reviews.
Simple example
Let's say we stored this review:
{
"rating": 3,
"title": "2009 Honda Fit",
"body": "I loved it and I will never buy any other brand of vehicle!",
"metafields": {
"tag": "Car",
"year": "2009",
"brand": "Honda",
"model": "Fit"
}
}
We could find anything else matching these metafields with a URL like the following:
https://demo.shiny.reviews/api/v1/reviews?token=[my_long_token]&metafields[tag]=Car&metafields[year]=2009&metafields[brand]=Honda&metafields[model]=Fit
A request to that URL would return the reviews that had all those metafields.
Advanced tag filter
You can also filter reviews that have one metafield value OR another.
If we wanted to search for a metafield that had a tag of "Car" OR "Truck", then we can do the following:
https://demo.shiny.reviews/api/v1/reviews?token=[my_long_token]&metafields[tag][0]=Car&metafields[tag][1]=Truck
That would look like this if you were using URLSearchParams
on the frontend to serialize your objects for you:
const params = new URLSearchParams({
"metafields[tag][0]": "Car",
"metafields[tag][1]": "Truck",
});
Sentiment analysis
Coming soon
Use AI to assign a rating for how positive or negative the review is.
Review algorithm
Coming soon
How the average rating should be calculated.
Profanity filter
Coming soon
Detect profanity.
Verified reviewer
Coming soon
Require the reviewer to use a verified email.
Image attachments
Coming soon
Allow images to be attached with the rewview.
Pre-built form widgets
Coming soon
Easily support the creation of reviews with a pre-supplied form
that can be loaded in an iframe
.
List all the details for the project. Requires a read token when "Public/unprotected" is enabled.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
"method": "GET",
headers,
}).then(response => response.json());
Example response (200):
{
"data": {
"code": "jsrin4x03",
"name": "Maggio, Heller and Schuppe",
"description": "Laudantium voluptatibus voluptatum alias natus suscipit. Eaque id quo et et eius.",
"domain": "https://www.gmail.com",
"features": {
"review-notification": true,
"metafields": true,
"sentiment-analysis": false,
"review-approval": true,
"public-unprotected": true,
"domain-locked": false,
"markdown": false
},
"review_stats": {
"average": 0,
"total_count": 0,
"approved_count": 0
},
"ratings_style": "stars",
"ratings_style_options": [
1,
2,
3,
4,
5
]
}
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List all the details for the project. Requires a read token when "Public/unprotected" is enabled.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1/project?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/project"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
"method": "GET",
headers,
}).then(response => response.json());
Example response (200):
{
"data": {
"code": "dk5o6f2uq",
"name": "Maggio, Heller and Schuppe",
"description": "Laudantium voluptatibus voluptatum alias natus suscipit. Eaque id quo et et eius.",
"domain": "https://www.gmail.com",
"features": {
"review-notification": true,
"metafields": true,
"sentiment-analysis": false,
"review-approval": true,
"public-unprotected": true,
"domain-locked": false,
"markdown": false
},
"review_stats": {
"average": 0,
"total_count": 0,
"approved_count": 0
},
"ratings_style": "stars",
"ratings_style_options": [
1,
2,
3,
4,
5
]
}
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Summarize all the reviews in the project based on a filter. Requires a read token when "Public/unprotected" is not enabled.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1/reviews/summary?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"metafields\": {
\"make\": \"Honda\"
}
}"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/reviews/summary"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"metafields": {
"make": "Honda"
}
};
fetch(url, {
"method": "GET",
headers,
"body": JSON.stringify(body),
}).then(response => response.json());
Example response (200):
{
"total_reviews": 184,
"average_rating": 2.462
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Used for approving reviews through emails. Requires a signed URL. Currently, this cannot be called directly.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1/reviews/1/approve?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/reviews/1/approve"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
"method": "GET",
headers,
}).then(response => response.json());
Example response (200):
{
"data": {
"id": 727,
"rating": 0,
"title": "Eos voluptas molestias aut laudantium voluptatibus voluptatum alias natus.",
"body": "Odit eaque id quo et et eius voluptatem. Accusantium aliquam dicta et et labore voluptate doloremque voluptatem. Vitae porro voluptatum odio. Beatae numquam voluptate et omnis in. Blanditiis et laudantium aut porro et.",
"metafields": null,
"created_at": "2024-11-11T23:51:16.000000Z"
}
}
Example response (302, review approved successfully):
Empty response
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List all the reviews in the project. Results are paginated. Requires a read token when "Public/unprotected" is not enabled.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1/reviews?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"metafields\": {
\"tag\": \"Product\"
},
\"positive\": true,
\"negative\": false
}"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/reviews"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"metafields": {
"tag": "Product"
},
"positive": true,
"negative": false
};
fetch(url, {
"method": "GET",
headers,
"body": JSON.stringify(body),
}).then(response => response.json());
Example response (200):
{
"data": [
{
"id": 728,
"rating": 0,
"title": "Eos voluptas molestias aut laudantium voluptatibus voluptatum alias natus.",
"body": "Odit eaque id quo et et eius voluptatem. Accusantium aliquam dicta et et labore voluptate doloremque voluptatem. Vitae porro voluptatum odio. Beatae numquam voluptate et omnis in. Blanditiis et laudantium aut porro et.",
"metafields": null,
"created_at": "2024-11-11T23:51:16.000000Z"
},
{
"id": 729,
"rating": 0,
"title": "Aperiam blanditiis voluptatem officia magnam rerum minima.",
"body": "Quam nisi quisquam sit quos. Laboriosam deleniti minima vitae assumenda rerum. Ut illum accusamus illo et et aperiam. Repellendus nostrum amet quo molestiae. Et ea numquam totam maxime ea provident magni quisquam.",
"metafields": null,
"created_at": "2024-11-11T23:51:16.000000Z"
}
],
"links": {
"first": "/?page=1",
"last": "/?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "/?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "/",
"per_page": 10,
"to": 2,
"total": 2
}
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Store a single review. Requires a write token.
Example request:
curl --request POST \
"https://[project_code].shiny.reviews/api/v1/reviews?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"rating\": 3,
\"title\": \"2009 Honda Fit\",
\"body\": \"I loved it and I will never buy any other brand of vehicle!\",
\"metafields\": {
\"tag\": \"Car\",
\"author\": \"Rando\",
\"approved\": true
}
}"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/reviews"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"rating": 3,
"title": "2009 Honda Fit",
"body": "I loved it and I will never buy any other brand of vehicle!",
"metafields": {
"tag": "Car",
"author": "Rando",
"approved": true
}
};
fetch(url, {
"method": "POST",
headers,
"body": JSON.stringify(body),
}).then(response => response.json());
Example response (200):
{
"data": {
"id": 730,
"rating": 0,
"title": "Voluptatibus eveniet voluptatem mollitia et magni nam cupiditate.",
"body": "Voluptatem ea numquam eos architecto. Aspernatur dolore et dignissimos quibusdam consectetur eos voluptas molestias. Laudantium voluptatibus voluptatum alias natus suscipit. Eaque id quo et et eius. Vel accusantium aliquam dicta et et labore voluptate doloremque.",
"metafields": null,
"created_at": "2024-11-11T23:51:16.000000Z"
}
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Show the details for a single review. Requires a read token when "Public/unprotected" is not enabled.
Example request:
curl --request GET \
--get "https://[project_code].shiny.reviews/api/v1/reviews/1?token=iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://[project_code].shiny.reviews/api/v1/reviews/1"
);
const params = {
"token": "iT1fTUMRpZxgMaTxo1R2xyBc4KW8WF2abXF5hNNo",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
"method": "GET",
headers,
}).then(response => response.json());
Example response (200):
{
"data": {
"id": 731,
"rating": 0,
"title": "Eos voluptas molestias aut laudantium voluptatibus voluptatum alias natus.",
"body": "Odit eaque id quo et et eius voluptatem. Accusantium aliquam dicta et et labore voluptate doloremque voluptatem. Vitae porro voluptatum odio. Beatae numquam voluptate et omnis in. Blanditiis et laudantium aut porro et.",
"metafields": null,
"created_at": "2024-11-11T23:51:16.000000Z"
}
}
Received response :
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.