I am trying to unsubscribe our clients from our email list when they enter information into a form on our website. It’s a VB.Net application written in Visual Studio 2005.
The pertinent bit of code is this: (DIMS done earlier in the code for this bit)
If Me.TextBox1.Text > "" Then
returnxml = DRM.CheckCCEmail(Me.TextBox1.Text.ToString)
mdoc = New XmlDocument
mdoc.LoadXml(returnxml)
Dim namemngr As New XmlNamespaceManager(mdoc.NameTable)
namemngr.AddNamespace("client", "http://www.w3.org/2005/Atom")
namemngr.AddNamespace("contact", "http://ws.constantcontact.com/ns/1.0/")
Dim Root As XmlNode = mdoc.SelectSingleNode("client:feed", namemngr)
mnode = mdoc.SelectSingleNode("//client:entry/client:content/contact:Contact/contact:Status", namemngr)
Try
If mnode.InnerText.Contains("Active") Then
mnode.InnerText = "Do Not Mail"
'mdoc.DocumentElement("//client:entry/client:content/contact:Contact/contact:Status").InnerText = "Do Not Mail"
'mnode = mdoc.SelectSingleNode("//client:entry/client:content/contact:Contact/contact:Status", namemngr)
mdoc.WriteTo(xw)
returnxml = sw.ToString
FinalResult = DRM.ExecuteCCUrl(Me.TextBox1.Text.ToString, returnxml)
End If
Catch ex As Exception
Dim error1 As String = ex.Message
Me.Label1.Visible = True
End Try
End If
Everything works fine when I retrieve the client’s info. I can change the status to Do Not Mail from Active no problem.
However when I try to post that information back to the constant contact api, I invariably get a 401 not authorized error.
I’m talking to the API on both sides of this and supplying authorization credentials the same way on both sides so I’m baffled why my requests would be authorized in the first call of the API but not authorized in my subsequent post of the same data (simply modified to show Do Not Mail as the status) back to the API. I’m given to understand this is the preferred method of effecting changes.
I’ve included both calls to the api below so you can see that I am basically using the same methods both times except that the first time I’m issuing a GET and the second call is a PUT operation.
I’ve replaced our APIkey, username and password data with XXXX but I can assure you they are the same in both pieces of code when they get executed.
These are the bits of the DRM object that gets called above that handle talking to the API.
Public Shared Function CheckCCEmail(ByVal emailadd As String) As String
Dim apikey As String = "XXXXXX"
Dim UName As String = "XXXXX"
Dim UPass As String = "XXXXX"
Dim Email As String = HttpUtility.UrlEncode(LCase(emailadd))
Dim uri As New Uri("https://api.constantcontact.com/ws/customers/" & UName & "/contacts?email=" & Email)
Dim LoginCredentials As CredentialCache = New CredentialCache
LoginCredentials.Add(New Uri("https://api.constantcontact.com/ws/customers/" & UName), "Basic", New NetworkCredential((apikey + ("%" + UName)), UPass))
If (uri.Scheme = uri.UriSchemeHttps) Then
Dim request As HttpWebRequest = HttpWebRequest.Create(uri)
request.Method = WebRequestMethods.Http.Get
request.Credentials = LoginCredentials
Dim response As HttpWebResponse = request.GetResponse()
Dim reader As New StreamReader(response.GetResponseStream())
Dim tmp As String = reader.ReadToEnd()
response.Close()
Return tmp
End If
End Function
Public Shared Function ExecuteCCUrl(ByVal emailadd, ByVal xmldata) As String
Dim apikey As String = "XXXXX"
Dim Uname As String = "XXXX"
Dim UPass As String = "XXXX"
Dim Email As String = HttpUtility.UrlEncode(LCase(emailadd))
Dim uri As New Uri("https://api.constantcontact.com/ws/customers/" & Uname & "/contacts?email=" & Email)
Dim LoginCredentials As CredentialCache = New CredentialCache
LoginCredentials.Add(New Uri("https://api.constantcontact.com/ws/customers/" & Uname), "Basic", New NetworkCredential((apikey + ("%" + Uname)), UPass))
If uri.Scheme = uri.UriSchemeHttps Then
Dim request As HttpWebRequest = HttpWebRequest.Create(uri)
request.ContentLength = xmldata.Length
request.ContentType = "application/x-www-form-urlencoded"
request.Method = WebRequestMethods.Http.Put
Dim writer As New StreamWriter(request.GetRequestStream)
writer.Write(xmldata)
writer.Close()
Dim oResponse As HttpWebResponse = request.GetResponse()
Dim reader As New StreamReader(oResponse.GetResponseStream())
Dim tmp As String = reader.ReadToEnd()
oResponse.Close()
Return tmp
Else
Return "Fail"
End If
End Function
Thanks in advance for any help.
Steve Wells
Programmer and Data Base Administrator
Described and Captioned Media Program
Solved! Go to Solution.
I was able to issue an http delete against a contact id using .NET 2.0 Framework. This code was initially written in C#.NET which I then ran through the .net code converter.
Please note that you need to insert your username, password, and apiKey in order for this to work. Also, this is currently set to delete contact "1" and you would want to adjust this to delete the appropriate contact.
I hope this helps. Please let me know if you have any other questions or concerns, or if this does not work for you. Thanks!
David J
Status field of a Contact is read-only and is not able to be modified. That said, this should not result in a 401 error.
If you're looking to move a Contact to Do Not Mail, you would need to execute a Delete request (http://community.constantcontact.com/t5/Documentation/Opting-out-a-Contact/ba-p/25117) on the Contact URI. This will change their status from Active to Do Not Mail.
The 401 response is only returned if there is a failure in authentication in some way. This could include an invalid API key, invalid API key/username format or an actual bad username and/or password combination. I would recommend taking VB out of the equation and trying to send a request manually through an open source RESTClient (such as: http://community.constantcontact.com/t5/Documentation/How-To-Use-RESTClient/ba-p/24915) to see if you can make the request work with just a simple tool. Once you have everything working correctly there, it is usually pretty easy to see where in a code flow the problem is failing.
So, I shouldn't have relied on the information here: http://community.constantcontact.com/t5/Documentation/Updating-Contact-Information/ba-p/25055
which states
To update a contact's information, the best way is to get details of an existing contact, modify the details, and use the resulting XML in a PUT method against the following URI: and
The request body you use must contain all elements that belong to a contact, which are returned through the GET method. By using this XML, you should modify only the information you would like to update, then pass the XML in your request.
To modify the contact's subscriptions to different contact lists, you need to update contents of <ContactLists> element.
You may want to update that information to make it clear that there are elements of the info you get that you cannot modify according to the directions given at the link supplied above.
The link you provided says I should use the delete method but doesn't give any details on what form the delete method needs to be in when I send it.
Here's what it says:
A Contact is opted-out by issuing the DELETE method on the contact resource:
https://api.constantcontact.com/ws/customers/{username}/contacts/{contact-id}
The call will return 204 No Content if it succeeds. This should be used if the contact has decided to unsubscribe from receiving all emails or has asked to stop sending all emails. Opted-out contacts become members of the Do-Not-Mail special list and is removed from all other contact lists.
Is there a link that documents what the xml is that needs to be sent to the API?
In order to delete that contact, you would need to issue an http delete to that contacts id, ie:
https://api.constantcontact.com/ws/customers/{username}/contacts/{contact-id}
There is no XML request that would need to be sent as the request body for this type of request. If I am misunderstanding your question please let me know and I will do my best to clarify. Thanks.
David J
I understand what you are saying now. You mean to do an http DELETE. Problem with that is that .NET 2.0's httpwebrequest object doesn't support the DELETE method. You can do GET, POST, PUT, CONNECT, etc. with it but it does not provide any support for the DELETE method.
I've tried this code:
Dim apikey As String = "45911072-77d5-4c67-84b3-a657edba8629"
Dim Uname As String = "DCMPNews"
Dim UPass As String = "xxxxxxxxxxxxxxxxxx"
Dim Email As String = HttpUtility.UrlEncode(LCase(emailadd))
Dim uri As New Uri("https://api.constantcontact.com/ws/customers/" & Uname & "/contacts/ & contactID")
Dim LoginCredentials As CredentialCache = New CredentialCache
LoginCredentials.Add(New Uri("https://api.constantcontact.com/ws/customers/" & Uname), "Basic", New NetworkCredential((apikey + ("%" + Uname)), UPass))
If uri.Scheme = uri.UriSchemeHttps Then
Dim request As HttpWebRequest = HttpWebRequest.Create(uri)
request.ContentLength = 0
request.ContentType = "application/xml"
request.Method = "DELETE"
Dim oResponse As HttpWebResponse = request.GetResponse()
Dim reader As New StreamReader(oResponse.GetResponseStream())
Dim tmp As String = reader.ReadToEnd()
oResponse.Close()
Return tmp
Else
Return "Fail"
End If
Because I saw a forum posting elsewhere where someone said they got the HTTPWEBREQUEST object to do a delete doing it this way. However, I still get the 401 - not authorized error when I try this.
I'm going to try using this sample XML with a POST method to see if this works.
<entry xmlns="http://www.w3.org/2005/Atom">
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts/101</id>
<title type="text">Contact: u86597@example.com</title>
<updated>2008-04-25T19:29:06.096Z</updated>
<author> </author>
<content type="application/vnd.ctct+xml">
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/" id="http://api.constantcontact.com/ws/customers/joesflowers/contacts/101">
<EmailAddress>u86597@example.com</EmailAddress>
<OptInSource>ACTION_BY_CUSTOMER</OptInSource>
<ContactLists>
</ContactLists>
</Contact>
</content>
</entry>
substituting my username, etc.
I know this is not the right thing to do but we only have 1 list (and considering the difficulty I have getting the API to work I'm pretty sure we will never have more than one list) so maybe this will work for us for now.
I was able to issue an http delete against a contact id using .NET 2.0 Framework. This code was initially written in C#.NET which I then ran through the .net code converter.
Please note that you need to insert your username, password, and apiKey in order for this to work. Also, this is currently set to delete contact "1" and you would want to adjust this to delete the appropriate contact.
I hope this helps. Please let me know if you have any other questions or concerns, or if this does not work for you. Thanks!
David J
That worked. Thanks.
I guess I'll never know why code that worked for a get didn't work for a delete.
I downloaded the REST client and tried to use it. I entered the following URL
https://api.constantcontact.com/ws/customers/DCMPNews/contacts/3107
and specified the DELETE method
I entered my apiKey + % + username and my password in the indicated fields on the AUTH tab of the REST Client 2.3.3
I got a 204 - no content so I guess that was the expected response. However, knowing that doesn't get me any closer to knowing why I get a 401 - not authorized error when I do the same thing from VB.NET. I'm using exactly the same code that I use when I get the client's info with a GET method. That method works every time and I get the expected XML back.
So, any ideas why the same credentials don't work when I'm using a DELETE or a POST method?
Announcements
Join our list to be notified of new features and updates to our V3 API.
Sign Up