We all started somewhere! Share your experience on the Get Advice: Let's Get Started Sweepstakes thread and be entered to win a $100 credit on your Constant Contact account.

401 Error on attempt to create Activity (OAuth)

Regular Participant

401 Error on attempt to create Activity (OAuth)

I'm creating a PHP application that will need to occasionally perform bulk updates of contacts on Constant Contact. So, I've been attempting to use the Activities API. 


Unfortunately, this has not been working out well. My current code to post, put, or get individual contacts (using OAuth) is working perfectly well. Theoretically, the only modifications I would have to make to the code to perform the HTTP POST is to properly encode the data as per the Activities documentation (which I have verified that I am doing), change the Content-Type in the header (x-www-form-urlencoded rather than application/atom+xml;type=entry), and of course, to post to the correct URI (for activities, I'm posting to https://api.constantcontact.com/ws/customers/<username>/activities).


The error I'm getting is 'HTTP Status 401 - Invalid signature for signature method HMAC-SHA1'. Based on past experience, this sort of error always came down to an error in the OAuth signing. Here, though, that's doesn't seem to be the case. The same code is responsible for posting xml data, and is working fine in that context. Any guidance on what a 401 error could point to here?


Sample post data to create the activity (exactly what is set to CURLOPT_POSTFIELDS):

activityType=SV_ADD&data=Email+Address%2CFirst+Name%2CLast+Name%2CHome+Phone%2CWork+Phone%2CAddress+Line+1%2CCity%2CUS+State%2CCountry%2CZip%2CSub+Zip%0Aheather.byington%40fayebsg.com%2CJason%2CStatham%2C%28818%29+227-5130%2C%28818%29+227-5130%2CSampleAddress%2CSampleCity%2CCA%2CUSA%2C91367%2C%0Asample.email%40nowhere.com%2CStatham%2CJason%2C%2C%2C%2CValencia%2CCA%2CUnited+States%2C91355%2C%0A&lists=http%3A%2F%2Fapi.constantcontact.com%2Fws%2Fcustomers%2F<url-coded username>%2Flists%2F1


Sample header (precisely what is set to CURLOPT_HTTPHEADER):

Array( [0] => Authorization: OAuth oauth_version="1.0",oauth_nonce="c1b3b920e884ce775749f84645973143",oauth_timestamp="1303103990",oauth_consumer_key="<key>",oauth_token="<token>",oauth_signature_method="HMAC-SHA1",oauth_signature="<sig>" [1] => Content-Type:application/x-www-form-urlencoded [2] => Content-Length: 516 )



Edit: Actually, here's some sample code as well.


This function performs the OAuth work:



function perform_signed_post($url, $postData, $contentType = 'application/atom+xml;type=entry') {
        $token = new OAuthToken($this->access_token, $this->access_token_secret);
        $token_request = OAuthRequest::from_consumer_and_token($this->oauth_consumer, $token, 'POST', $url);
        $token_request->sign_request($this->sig_method, $this->oauth_consumer, $token);
        $token_request_header = $token_request->to_header();
        $response = self::perform_curl_post($token_request_header, $url, $postData, $contentType);
        return $response;





This function does the actual HTTP POST:


public static function perform_curl_post($auth_header, $url, $postData, $contentType)
        $session = curl_init($url);
        curl_setopt($session, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($session, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($session, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($session, CURLOPT_POST, 1);
        curl_setopt($session, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($session, CURLOPT_HTTPHEADER, array($auth_header,'Content-Type: '.$contentType, 'Content-Length: '.strlen($postData)));
        $response = curl_exec($session);
        $ret = '';
        if(!$response) {
            $ret = array('error' => curl_error($session), 'status' => curl_getinfo($session, CURLINFO_HTTP_CODE), 'data' => null);
        else {
            $ret = array('error' => null, 'status' => curl_getinfo($session, CURLINFO_HTTP_CODE), 'data'=>$response);
        return $ret;




It appears that this might actually be an issue on our end as I am running into the same issue posting this activity, however when using basic authentication it does seem to work properly. I am going to discuss this with our engineering team and will get back to you as soon as I have anymore information on this. I apologize for any inconvenience that this has caused.

David J

Regular Participant

Only a minor inconvenience so far, there's always other work to do. :) I appreciate the quick response. If I may ask another question in this same thread (I'll make another one if necessary):


This application needs to use oob authentication. Unfortunately, when a user enters their credentials and presses the Confirm Access button, they are redirected to a 403 Forbidden page. The verifier is present in the query string, and using it does allow access, but it's a pretty scary thing for users to encounter. This only recently (last couple weeks) started happening, with no modifications to our application.

Regular Participant

Any update on this?

Our engineering team is looking at this defect and we plan to have it fixed in an upcoming release.  At this time, we are not able to provide any dates as to when this will be.  I will follow up on this with our team to try to get a better time estimate on this.


As an aside, the oob option for OAuth is indeed a valid flow for users to go through.  For usability purposes though, this is the least recommended flow.  Using a callback URL to process the verify token automatically without any user interaction is always prefered as it makes the flow easier for them to understand and follow.  Is there a technical limitation that is preventing this flow for your application?  I'd be interested in hearing more about the design flows that prompted you to choose the oob option instead of a callback URL.

Dave Berard
Senior Product Manager, Constant Contact
Regular Participant

Yeah, there's a technical reason. The application we're integrating Constant Contact with is sometimes operated in a hosted environment, so in many circumstances, we wouldn't need to use the oob flow. However, it is also commonly operated on an internal intranet, and is not visible from outside that network, so we need to make the oob flow an option.

There is one additional option that you can use, this is the same flow that a program like Tweetdeck or other installed Twitter/Facebook clients use.


The change you make is to have the intranet applications that are not visable to the word have an end point that is visible, such as your company website.  You can connect to this through your intranet and then make the callback URL point to that website endpoint.  Once the website receives the verifier, you can then pass it back through your intranet to the application. 


This additional step would be fairly easy to handle by using the optional parameter feature of OAuth.  You can set up something like clientid=XXXXXX, pass that as one of the parameters of request and per the OAuth standard, we have to pass that back as part of the callback URL.  This way, you can identify the correct client application that needs to receive this information and pass it back to them. 


I hope to have an update on this defect today, will update once I do.

Dave Berard
Senior Product Manager, Constant Contact

We're seeing the same behaviour: an activity POST action to populate a list is failing with OAuth, but works with the username/password mode of authentication. It must be related to decoding the form data, since if I replace the data parameter in the form with "data=something" I get a 500 error (not 401). Also, the clear list activity posts just fine with OAuth.


This is a pretty major blocker for us. Any timeline on a fix, or workaround to populate a list in bulk with OAuth?




Developer Portal

View API documentation, code samples, get your API key.

Visit Page