Why would you need to store a unique identifier for each email you send? You just need a unique identifier for each user, as settings would surely be user-specific.
Typically, email subscription settings wouldn't be considered high-risk or high-security - after all, they can simply resubscribe if they want the email. I would encrypt the user's id (and maybe their email address or some other salt) to give each user a unique unsubscribe key that is added into a link in the footer of each email.
Storing a unique key per user in a database shouldn't concern you. Chances are if you have the resources to send mass mailings on a regular bases, you have the resources to store a 32 character key.
Edited to address question updates, previous answer removed
Looking over your changes to your question I think I understand the problem you are facing a bit more. As there is no field that is an identifier on your resources (just a link) you have no way to refer to that specific resource within your GUI (i.e. a link to a page describing a specific pet).
The first thing to determine is if a pet ever makes sense without an owner. If we can have a pet without any owner then I would say we need some sort of unique property on the pet that we can use to refer to it. I do not believe this would violate not exposing the ID directly as the actual resource ID would still be tucked away in a link that the REST client wouldn't parse. With that in mind our pet resource may look like:
<Entity type="Pet">
<Link rel="self" href="http://example.com/pets/1" />
<Link rel="owner" href="http://example.com/people/1" />
<UniqueName>Spot</UniqueName>
</Entity>
We can now update the name of that pet from Spot to Fido without having to mess with any actually resource IDs throughout the application. Likewise we can refer to that pet in our GUI with something like:
http://example.com/GUI/pets/Spot
If the pet does not make any sense without an owner (or pets are not allowed in the system without an owner) then we can use the owner as part of the "identity" of the pet in the system:
http://example.com/GUI/owners/John/pets/1 (first pet in the list for John)
One small note, if both Pets and People can exist separate of each-other I would not make the entry point for the API the "People" resource. Instead I would create a more generic resource that would contain a link to People and Pets. It could return a resource that looks like:
<Entity type="ResourceList">
<Link rel="people" href="http://example.com/api/people" />
<Link rel="pets" href="http://example.com/api/pets" />
</Entity>
So by only knowing the first entry point into the API and not processing any of the URLs to figure out system identifiers we can do something like this:
User logs into the application. The REST client accesses the entire list of people resources available which may look like:
<Entity type="Person">
<Link rel="self" href="http://example.com/api/people/1" />
<Pets>
<Link rel="pet" href="http://example.com/api/pets/1" />
<Link rel="pet" href="http://example.com/api/pets/2" />
</Pets>
<UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
<Link rel="self" href="http://example.com/api/people/2" />
<Pets>
<Link rel="pet" href="http://example.com/api/pets/3" />
</Pets>
<UniqueName>Jane</UniqueName>
</Entity>
The GUI would loop through each resource and print out a list item for each person using the UniqueName as the "id":
<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>
While doing this it could also process each link that it finds with a rel of "pet" and get the pet resource such as:
<Entity type="Pet">
<Link rel="self" href="http://example.com/api/pets/1" />
<Link rel="owner" href="http://example.com/api/people/1" />
<UniqueName>Spot</UniqueName>
</Entity>
Using this it can print a link such as:
<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>
or
<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>
If we go with the first link and assume that our entry resource has a link with a relation of "pets" the control flow would go something like this in the GUI:
- Page is opened and the pet Spot is requested.
- Load the list of resources from the API entry point.
- Load the resource that is related with the term "pets".
- Look through each resource from the "pets" response and find one that matches Spot.
- Display the information for spot.
Using the second link would be a similar chain of events with the exception being that People is the entry point to the API and we would first get a list of all people in the system, find the one that matches, then find all pets that belong to that person (using the rel tag again) and find the one that is named Spot so we can display the specific information related to it.
Best Answer
For a simple solution that doesn't require you to change the links in the emails, you could have the relevant part of your API respond to requests from user agents in a way that makes sense to an end user.
Have your API respond to requests with an
accept
header containingtext/html
by redirecting them to a remote resource specified by the business. Allow the businesses to set up the redirect path (with some simple token replacement) through another part of the API.If the business hasn't set up a redirect path, serve up an HTML page containing the content of the enquiry instead of redirecting the request.
Of course, this only needs to be done for the part of the API that's linked to in the emails.
This approach has the advantage of businesses being able to change the redirect path at any time, and if a customer clicks on a link in an old email, they will get sent to the new remote resource. This also allows you to do click tracking if you want to.
Here's an example of how it might work:
Your client "Business A" wants to show enquiries on their own dashboard, so they set up a redirect path by making a request:
PUT /v1/businesses/business_a/enquirypath
, containing{"path":"http://business_a.com/dashboard/enquiries/{id}"}
.Your client "Business B" doesn't have their own dashboard, so they don't set up a redirect path.
You send out emails on behalf of businesses A and B. The emails link to the same resources that your API uses.
An end user clicks the link in the email from Business B. Your server sees that HTML content is being requested (rather than JSON/XML). Business B has not indicated that requests should be redirected, so the API simply serves up the enquiry in user-friendly HTML form, like the user agent requested.
An end user clicks the link in the email from Business A. Your server sees that HTML content is being requested again. Business A has indicated that enquiry requests should be redirected to their own server, so your API redirects the request to the path they specified.