Skip to main content

How To Create Story Document URL Redirects Using Draft API

This guide demonstrates how to create, update, or delete a Redirect, and how various operations you perform on a document can affect a Redirect. To read more about redirect methodologies in Arc XP, see Configuring URL redirects.

Prerequisites

Use Case

Arc XP stories have one primary, or canonical, URL. The canonical URL is the URL the system gives to the story when you circulate the story to its primary website section. When you circulate a story to additional websites, each website also has a URL associated with it. You may want to attach still more URLs to a story. The Draft API provides you a way to programmatically create and associate these additional URLs. In the Draft API, these additional URLs are called Document Redirects, also known as Vanity Redirects.

When you are transferring content from an existing CMS, and you find the need to redirect from legacy URLs to content on the new website, you can creating a redirect programmatically using the Draft API.  For this use case we start with this situation:

  • The Arc ID of a story in the organization cyclist-daily migrating into Cyclist Daily website is 4XEQFBHOI5EQRNE4CADZWCO37U.

  • The story's canonical URL on Cyclist Daily is /outdoor-bikes/2019/11/25/how-to-fix-your-bike/.  

  • The story has been shared to Facebook and it was given another URL for that use, /social/the-bike-fix-20191125/.

You create a redirect URL to keep the pre-existing social URL from generating a 404 Not Found error when the content goes live on Arc XP. 

Creating a Story Document Redirect

We start the guide by creating a simple story document, on which we perform a series of workflows.

curl --request POST \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
      "type": "story",
      "version": "0.10.2",
      "_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "canonical_website": "cyclist-daily",
      "headlines": {
          "basic": "How to fix your bike"
        }
    }'

# Response
{
  "id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "type": "STORY",
  "created_at": "2019-11-25T16:15:04.135Z",
  "draft_revision_id": "PQ4N3QHBARHPTGLC23NC33X4TA"
}

When you create a story, the story document is not yet published or circulated.

To create a redirect to the story document, we could send a request to the Draft API /redirect endpoint.

https://api.{org}.arcpublishing.com/draft/v1/redirect/{website}/{URL that needs to redirect to a story document}

But if we try to do that:

curl --request POST \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/redirect/cyclist-daily/social/the-bike-fix-20191125/ \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U"
  }'

# Response
{
  "request_id": "1-5db353be-386cbe7c90db8dbc9e727700",
  "error_message": "cannot create redirect to an uncirculated document",
  "error_code": "ErrInvalidData"
}

We are returned an error explaining that a redirect can be created only if the story is already circulated. This is the correct behavior, and it's done to ensure that a redirect object pointing to a non-existing URL is never created.

Instead, let’s circulate the story using the Draft API /circulation endpoint before trying again to create a redirect.

curl --request PUT \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/circulation/cyclist-daily \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
    "website_url": "/outdoor-bikes/2019/11/25/how-to-fix-your-bike/",
    "website_primary_section": {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/outdoor-bikes",
        "website": "cyclist-daily"
      }
    },
    "website_sections": [
      {
        "type": "reference",
        "referent": {
          "type": "section",
          "id": "/outdoor-bikes",
          "website": "cyclist-daily"
        }
      }
    ]
  }'

# Response
{
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "website_id": "cyclist-daily",
  "website_url": "/outdoor-bikes/2019/11/25/how-to-fix-your-bike/",
  "website_primary_section": {
    "type": "reference",
    "referent": {
      "type": "section",
      "id": "/outdoor-bikes",
      "website": "cyclist-daily"
    }
  },
  "website_sections": [
    {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/outdoor-bikes",
        "website": "cyclist-daily"
      }
    }
  ]
}

Now the story document with the Arc ID 4XEQFBHOI5EQRNE4CADZWCO37U is circulated in the section /outdoor-bikes for the website cyclist-daily with the canonical URL /outdoor-bikes/2019/11/25/how-to-fix-your-bike/.

We can confirm this by querying for the circulation data at the Draft API /circulation endpoint.

curl --request GET \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/circulation \
  --header 'Authorization: Bearer <token>' 

# Response
{
  "circulations": [
    {
      "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "website_id": "cyclist-daily",
      "website_url": "/outdoor-bikes/2019/11/25/how-to-fix-your-bike/",
      "website_primary_section": {
        "type": "reference",
        "referent": {
          "type": "section",
          "id": "/outdoor-bikes",
          "website": "cyclist-daily"
        }
      },
      "website_sections": [
        {
          "type": "reference",
          "referent": {
            "type": "section",
            "id": "/outdoor-bikes",
            "website": "cyclist-daily"
          }
        }
      ]
    }
  ]
}

Now that our story is circulated, we can try to create that redirect again.

https://api.{org}.arcpublishing.com/draft/v1/redirect/{website}/{URL that needs to redirect to a story document}

curl --request POST \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/redirect/cyclist-daily/social/the-bike-fix-20191125/ \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U"
  }'

# Response
{
  "website_id": "cyclist-daily",
  "website_url": "/social/the-bike-fix-20191125/",
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "created_at": "2019-11-25T16:18:22.714Z",
  "updated_at": "2019-11-25T16:18:22.714Z"
}

The redirect was successfully created. We now have the URL /social/the-bike-fix-20191125/ pointing to the document 4XEQFBHOI5EQRNE4CADZWCO37U.

Verifying Redirect URLs in the Draft API

You can test the validity of a specific redirect URL using a GET to the Draft API /redirect endpoint.

https://api.{org}.arcpublishing.com/draft/v1/redirect/{website}/{redirect URL}

A 200 response code at this endpoint with these parameters tells you that the URL you tested is:

  • a story document URL

  • a URL that is a redirect but is not the URL that a story has been circulated to directly, for example, a canonical URL

  • a URL that that points to a particular story document ID.  

# test using the redirect url
curl --request GET \
  --url https://api.sandbox.arctesting1.arcpublishing.com/draft/v1/redirect/arc_test1/social/the-bike-fix-20191125/ \
  --header 'Authorization: Bearer <token>'

# Response
{
  "id": "ZXYTUVWABCDEFG234567899876",
  "website_id": "arc_test1",
  "website_url": "/social/the-bike-fix-20191125/",
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "created_at": "2019-11-25T16:18:22.714Z",
  "updated_at": "2019-11-25T16:18:22.714Z"
}

If you try to return from this endpoint a canonical URL instead of a redirect, or the URL of a video or gallery document, the endpoint would not return a success code.

# test using the canonical URL
curl --request GET \
  --url https://api.sandbox.arctesting1.arcpublishing.com/draft/v1/redirect/outdoor-bikes/2019/11/25/how-to-fix-your-bike/ \
  --header 'Authorization: Bearer <token>'
  
# Response
{
    "request_id": "1-644fe728-4a3fc4ef6cca69a616a3755f",
    "error_message": "no redirect found for /outdoor-bikes/2019/11/25/how-to-fix-your-bike/",
    "error_code": "ErrNotFound"
}

You can verify if a story has any redirects associated with it by using a variation of the /redirect endpoint. If the story document has multiple redirects, you will see them all in the response.

https://api.{org}.arcpublishing.com/draft/v1/story/{arc id}/redirect/{website}/

curl --request GET \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/redirect/cyclist-daily \
  --header 'Authorization: Bearer <token>' \

# Response
{
  "redirects": [
    {
      "website_id": "cyclist-daily",
      "website_url": "/social/the-bike-fix-20191125/",
      "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "created_at": "2019-11-13T17:09:00.381Z",
      "updated_at": "2019-11-13T17:09:00.381Z"
    }
  ]
}

Decirculating: Removing the Canonical URL

To de-circulate the story document from the website, let’s start by de-circulating the story from the cyclist-daily website with a DELETE call to the Draft API /circulation endpoint.

curl --request DELETE \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/circulation/cyclist-daily
  --header 'Authorization: Bearer <token>' \

# Response
{
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "website_id": "cyclist-daily",
  "website_url": "/outdoor-bikes/2019/11/25/how-to-fix-your-bike/",
  "website_primary_section": {
    "type": "reference",
    "referent": {
      "type": "section",
      "id": "/outdoor-bikes",
      "website": "cyclist-daily"
    }
  },
  "website_sections": [
    {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/outdoor-bikes",
        "website": "cyclist-daily"
      }
    }
  ]
}

The de-circulation response brings back the circulation information that was just removed. However, if you follow with a GET to the /circulation endpoint for the story once more, your response is an empty array. After de-circulation, the URL /outdoor-bikes/2019/11/25/how-to-fix-your-bike/ is not externally accessible.

Note

The redirects to the story on the de-circulated cyclist-daily website ARE NOT deleted and continue to redirect to the same document ID, 4XEQFBHOI5EQRNE4CADZWCO37U. This preservation of redirects enables re-use in the event of the story being re-circulated to cyclist-daily.

This means that if a user tries to access the redirect URL, /social/the-bike-fix-20191125/, the system redirects to a de-circulated story, resulting in a 404 Not Found error. This behavior is as expected. While the Draft API does not allow you to create new redirects when a story is not circulated, existing redirects are not deleted if the story is later de-circulated.

You can verify the preserved redirect can be found by fetching it from the Draft API /redirect endpoint, even after de-circulation. This redirect object is still available, but pointing to a de-circulated story document. You can delete redirects with a DELETE to the Draft API /redirect endpoint.

Recirculating a Document

Let's fix the story so it again has a canonical URL and the redirect we made works. But rather than re-create the original, let's explore circulating the story to a different address.  

curl --request PUT \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/circulation/cyclist-daily \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "website_url": "/do-it-yourself/how-to-fix-your-bike/",
    "website_primary_section": {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/do-it-yourself",
        "website": "cyclist-daily"
      }
    },
    "website_sections": [
      {
        "type": "reference",
        "referent": {
          "type": "section",
          "id": "/do-it-yourself",
          "website": "cyclist-daily"
        }
      }
    ]
  }'

# Response
{
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "website_id": "cyclist-daily",
  "website_url": "/do-it-yourself/how-to-fix-your-bike/",
  "website_primary_section": {
    "type": "reference",
    "referent": {
      "type": "section",
      "id": "/do-it-yourself",
      "website": "cyclist-daily"
    }
  },
  "website_sections": [
    {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/do-it-yourself",
        "website": "cyclist-daily"
      }
    }
  ]
}

The story is now successfully circulated again, this time to the section /do-it-yourself and the URL /do-it-yourself/how-to-fix-your-bike/.

If we go check the story's redirects, we can see the previously created redirect is still there, pointing to the same Document ID, which is our now re-circulated story.

curl --request GET \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/redirect/cyclist-daily \
  --header 'Authorization: Bearer <token>' \

# Response
{
  "redirects": [
    {
      "website_id": "cyclist-daily",
      "website_url": "/social/the-bike-fix-20191125/",
      "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "created_at": "2019-11-13T17:09:00.381Z",
      "updated_at": "2019-11-13T17:09:00.381Z"
    }
  ]
}

Recirculating: From Canonical URL to Redirect URL

Now, let’s imagine a scenario where we want to circulate an already-circulated document to a new canonical URL.

This workflow turns the current canonical URL into a redirect object pointing to a Document ID, then creates a new canonical URL and circulates the story to the new website section and URL.

From our previous API calls in this guide, we know we have a story document circulated at the address /do-it-yourself/how-to-fix-your-bike/ with a redirect object /social/the-bike-fix-20191125/ pointing to the Document ID 4XEQFBHOI5EQRNE4CADZWCO37UP.

Let’s see what happens when we try to circulate the story to the address /broken-bikes/how-to-fix-your-bike/:

curl --request PUT \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/story/4XEQFBHOI5EQRNE4CADZWCO37U/circulation/cyclist-daily \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "website_url": "/broken-bikes/how-to-fix-your-bike/",
    "website_primary_section": {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/broken-bikes",
        "website": "cyclist-daily"
      }
    },
    "website_sections": [
      {
        "type": "reference",
        "referent": {
          "type": "section",
          "id": "/broken-bikes",
          "website": "cyclist-daily"
        }
      }
    ]
  }'

# Response
{
  "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
  "website_id": "cyclist-daily",
  "website_url": "/broken-bikes/how-to-fix-your-bike/",
  "website_primary_section": {
    "type": "reference",
    "referent": {
      "type": "section",
      "id": "/broken-bikes",
      "website": "cyclist-daily"
    }
  },
  "website_sections": [
    {
      "type": "reference",
      "referent": {
        "type": "section",
        "id": "/broken-bikes",
        "website": "cyclist-daily"
      }
    }
  ]
}

Now, when we check the list of all the story's redirects, we can see that the previous canonical URL has become an additional redirect to the story document 4XEQFBHOI5EQRNE4CADZWCO37U.

{
  "redirects": [
    {
      "website_id": "cyclist-daily",
      "website_url": "/do-it-yourself/how-to-fix-your-bike/",
      "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "created_at": "2019-11-25T16:30:25.872Z",
      "updated_at": "2019-11-25T16:32:43.623Z"
    },
    {
      "website_id": "cyclist-daily",
      "website_url": "/social/the-bike-fix-20191125/",
      "document_id": "4XEQFBHOI5EQRNE4CADZWCO37U",
      "created_at": "2019-11-25T16:30:25.762Z",
      "updated_at": "2019-11-25T16:32:43.623Z"
    }
  ]
}

FAQs

What is the difference between a Vanity Redirect and a Document Redirect?

These terms are synonyms, with Vanity Redirect being an older reference. In past versions of the Draft API, the calls to create vanity redirects were different than the process described in this guide. This guide places more emphasis on the preferred terminology, Document Redirect, as it is more descriptive of the actual process that happens when additional URLs are added for one story document.

What is the difference between a Document Redirect and an External Redirect?

When you need to have a URL that operates from your hosted domain name, but targets a site that is external to Arc XP, this is the use case for an external redirect. The difference is that in the POST body, you refer to redirect_to: external website address instead of document_id: arc id.

If the URL http://www.cyclist-daily.org/bicycle-conference/ must exist and be served by Arc XP, but instead of pointing to an Arc XP story document it redirects the user to a Salesforce site, you could create this external redirect:

curl --request POST \
  --url https://api.cyclist-daily.arcpublishing.com/draft/v1/redirect/arc_test1/patti-external-redirect/ \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "redirect_to": "http://www.salesforce.com/cyclist-daily/bicycle-conference"
}'

# Response
{
    "id": "N4E4ML5EONC47OTBXZJ3E3LTVY",
    "website_id": "cyclist-daily",
    "website_url": "/bicycle-conference/",
    "redirect_to": "http://www.salesforce.com/cyclist-daily/bicycle-conference",
    "created_at": "2019-05-02T16:28:41.044Z",
    "updated_at": "2019-05-02T16:28:41.044Z"
}

Is it possible to create Vanity Redirects for Videos and Galleries?

The Draft API endpoint, /media-urls/v1/redirect/vanity, registers redirects to URLs that belong to videos and galleries. This endpoint does not work with story documents.

curl --request POST \
  --url https://api.cyclist-daily.arcpublishing.com/media-urls/v1/redirect/vanity \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
     "content_id": "id90b30a52-f654-4f09-82f9-b1da91013642",   # document id of a video or gallery
     "original_url": "/video-test-redirect/",     # url that will redirect to the video or gallery
     "destination_url": "/2023/02/14/imported-ans-video/"  # canonical URL of the video or gallery
}'

# Response
{
    "url": "/video-test-redirect/",
    "redirect_to": "/2023/02/14/imported-ans-video/",
    "created_date": "2023-05-02T01:27:14.918Z",
    "last_updated_date": "2023-05-02T01:27:14.918Z"
}