Organizing Content

This section explains how to use the Connect Server APIs to organize your content using tags and vanity URLs. See the Deploying Content section for help creating and deploying content, and the Managing Content section for help discovering and managing existing content.

Note

These recipes use bash snippets and rely on curl to perform HTTP requests. We use the CONNECT_SERVER and CONNECT_API_KEY environment variables introduced in the Getting Started section of this cookbook.

Tags

You can organize your content on Connect using a hierarchy of labels called “tags”. Tags and content items have a many-to-many relationship: any content item may be associated with multiple tags, and any tag may be associated with multiple content items. This gives you the flexibility to group and organize your content in whatever way best suits your organization.

Important

Creating, updating, and deleting tags require admin permissions; however, assigning and unassigning tags to content items only requires owner or collaborator permissions on the content item.

You can find the full reference of tag-related API endpoints in the Posit Connect API Reference.

Creating a Tag

Let’s assume that we would like to create a new hierarchy of tags for our finance department to track their daily reports. First, we will create a “Finance” tag:

export DATA='{"name": "Finance"}'
curl --silent --show-error -L --max-redirs 0 --fail -X POST \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    --data "${DATA}" \
    "${CONNECT_SERVER}__api__/v1/tags"
# => {
# =>   "id": "14",
# =>   "name": "Finance",
# =>   "parent_id": null,
# =>   ...
# => }

And now we can create another tag named “Daily” under the “Finance” tag by specifying the parent_id with the “Finance” tag’s id:

export DATA='{"name": "Daily", "parent_id": "14"}'
curl --silent --show-error -L --max-redirs 0 --fail -X POST \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    --data "${DATA}" \
    "${CONNECT_SERVER}__api__/v1/tags"
# => {
# =>   "id": "15",
# =>   "name": "Daily",
# =>   "parent_id": "14",
# =>   ...
# => }

Finding a Tag

We can easily find a tag by id using the GET /v1/tags/{id} endpoint:

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/tags/15"
# => {
# =>   "id": "15",
# =>   "name": "Daily",
# =>   "parent_id": "14",
# =>   ...
# => }

If we do not know the id of the tag, but we know the hierarchy of names for the tag, then we can also search by that “path” (which should be comma-separated) instead of id. For example:

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/tags/Finance,Daily"
# => {
# =>   "id": "15",
# =>   "name": "Daily",
# =>   "parent_id": "14",
# =>   ...
# => }

Assigning a Tag to a Content Item

You can easily assign tags to content items using the POST /v1/content/{guid}/tags endpoint. Let’s assign the “Daily” tag to a content item:

Note

When assigning tags to content, all tags “above” the specified tag in the hierarchy are automatically also associated with the content item. For example, if we have “Finance” -> “Daily” as tags, and we assign the Daily tag to a content item, then it will also be associated with the Finance tag.

export DATA='{"tag_id": "15"}'
curl --silent --show-error -L --max-redirs 0 --fail -X POST \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    --data "${DATA}" \
    "${CONNECT_SERVER}__api__/v1/content/ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5/tags"
# =>

A successful request will return a 200 HTTP status code with an empty response.

Listing Content Associated with Tags

We can see what content is associated with a given tag using the GET /v1/tags/{id}/content endpoint.

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/tags/15/content"
# => [
# =>   {
# =>     "guid": "ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5",
# =>     "name": "shakespeare",
# =>     "title": "Shakespeare Word Clouds",
# =>     ...
# =>   }
# => ]

Listing Tags Associated with Content

Likewise, we can see what tags are associated with a given content item using the GET /v1/content/{guid}/tags endpoint.

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/content/ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5/tags"
# => [
# =>   {
# =>     "id": "14",
# =>     "name": "Finance",
# =>     "parent_id": null,
# =>     ...
# =>   },
# =>   {
# =>     "id": "15",
# =>     "name": "Daily",
# =>     "parent_id": "14",
# =>     ...
# =>   }
# => ]

Vanity URLs

Vanity URLs allow you to assign custom paths to content items, providing more memorable and descriptive paths to your popular content items. Vanity URLs also let you take advantage of advanced deployment strategies such as Blue-Green Deployments.

You can find the full reference of vanity-related API endpoints in the Posit Connect API Reference.

Setting a Vanity URL for a Content Item

If you’d like to set a vanity URL for a specific content item, you should use the PUT /v1/content/{guid}/vanity endpoint.

Note

A content item may only have one vanity URL. Likewise, a unique vanity URL may only correspond to one content item. If you’d like to re-assign a vanity URL within a single API call, you can use the force parameter as outlined in the API Reference docs.

export DATA='{"path": "/shakespeare/"}'
curl --silent --show-error -L --max-redirs 0 --fail -X PUT \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    --data "${DATA}" \
    "${CONNECT_SERVER}__api__/v1/content/ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5/vanity"
# => {
# =>   "content_guid": "ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5",
# =>   "path": "/shakespeare/",
# =>   ...
# => }

Getting a Vanity URL for a Content Item

You can easily fetch the vanity URL for a specific content item as well using the GET /v1/content/{guid}/vanity endpoint.

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/content/ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5/vanity"
# => {
# =>   "content_guid": "ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5",
# =>   "path": "/shakespeare/",
# =>   ...
# => }

If there is no vanity URL set, you will receive a 404 HTTP status code.

Removing a Vanity URL from a Content Item

Also, you can remove a vanity URL from a content item using the DELETE /v1/content/{guid}/vanity endpoint.

curl --silent --show-error -L --max-redirs 0 --fail -X DELETE \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/content/ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5/vanity"
# =>

A successful request will return a 204 HTTP status code with an empty response.

Listing All Vanity URLs

Lastly, you may want to list all vanity URLs across all content on the Connect server. This can be useful for auditing or searching for content by URL. The GET /v1/vanities endpoint provides this functionality.

curl --silent --show-error -L --max-redirs 0 --fail -X GET \
    -H "Authorization: Key ${CONNECT_API_KEY}" \
    "${CONNECT_SERVER}__api__/v1/vanities"
# => [
# =>   {
# =>     "content_guid": "ccbd1a41-90a0-4b7b-89c7-16dd9ad47eb5",
# =>     "path": "/shakespeare/",
# =>     ...
# =>   },
# =>   ...
# => ]