Subscription Events

AppDirect has recently released an Open Source SDK for our Distribution APIs that developers can use. Please see here for more information

Subscriptions are created when the customer places their order. Once the user has created a subscription, they may update or cancel their subscription. Additionally, AppDirect provides information about a customer's overdue or deliquent subscriptions.

Developers must register four URL endpoints to handle notifications for these subscription management events. (The developer can implement a URL for each event type or use a single URL to handle all of them.) When an event occurs, AppDirect will notify you by calling this URL, which is hosted by you, and passing an eventUrl parameter you will then use to fetch information about this event. The eventUrl parameter identifies the location of an event, which then can be used to fetch information about that event. Upon notification, you can perform an HTTP GET to the eventUrl to read an XML or JSON representation of the event.

These subscription URL endpoints are configured under the "Edit Integration Settings" section when you edit your product in the Developer Portal. We highly recommend that developers use HTTPS endpoints secured with SSL certificates. Such certificates must be publically signed.

AppDirect's APIs support "two-legged" OAuth 1.0, also known as "signed fetch." You need to sign calls to this API using "2-legged OAuth". The OAuth consumer key and secret can be found under the "Edit Integration Settings" section. Additional information about using OAuth can be found here.

Subscription Lifecycle

AppDirect manages the lifecycle of subscriptions to products purchase through the marketplace. A subscription can be in one the following states:

  • FREE_TRIAL: the subscription is in free trial, the customer is not being charged.
  • FREE_TRIAL_EXPIRED: the free trial has expired, the customer cannot use his subscription unless he converts to a paid subscription.
  • ACTIVE: regular subscription in "good standing". If the application is not free, the customer is being changed the appropriate amount.
  • SUSPENDED: an invoice for this subscription is overdue. The subscription cannot be used unless the customers pays the invoice.
  • CANCELLED: the subscription is not active anymore and does not show up in the customer marketplace account.

Subscribing to a product

The entry state for a subscription can either be FREE_TRIAL (if the edition purchased by the user has a free trial period) or ACTIVE. When a user purchases a subscription to a product, the AppDirect powered marketplace will notify the ISV by triggering a SUBSCRIPTION_ORDER event.

Free Trial Support

A subscription in the FREE_TRIAL state will move to the FREE_TRIAL_EXPIRED state once the free trial expires and if the user doesn't convert to a paid subscription. In this case, the marketplace will notify the ISV by triggering a SUBSCRIPTION_NOTICE event, with a notice type DEACTIVATED and containing the new subscription state.

On the other hand, if a customer decides to convert a free trial subscription to a paid one (before or after the free trial expires), the subscription state will change to ACTIVE and the marketplace will notify the ISV by triggering a SUBSCRIPTION_NOTICE event, with a notice type REACTIVATED and containing the new subscription state.

Delinquent Subscriptions

By default, AppDirect will consider a subscription to be delinquent when 3 payments have been missed.

When a customer misses the payment of an invoice for a subscription in the ACTIVE state, the subscription state changes to SUSPENDED and the marketplace will notify the ISV by triggering a SUBSCRIPTION_NOTICE event, with a notice type DEACTIVATED and containing the new subscription state.

When the customer pays the overdue invoice, the subscription state changes back to ACTIVE and the marketplace will notify the ISV by triggering a SUBSCRIPTION_NOTICE event, with a notice type REACTIVATED and containing the new subscription state.

When the customer fails to pay the overdue invoice, the customer's subscription in the AppDirect marketplace may be closed, triggering a CLOSED notice type. The ISV should not delete a delinquent subscription unless notified by AppDirect that a subscription has been closed.

Modifying a Subscription

A customer can modify the items ordered for subscriptions to products including metered items (where the customer has to choose the number of seats purchased, or some other items at purchase time). In this case the marketplace will notify the ISV by triggering a SUBSCRIPTION_CHANGE event, containing an updated list of the new ordered items.

Subscription End of Life

A subscription can come to its end of life, which is the CANCELLED state one of two ways: the customer decides to end his subscription by going to his marketplace account and cancelling it. In this case, the marketplace will notify the ISV by triggering a SUBSCRIPTION_CANCEL event, after which he subscription is cleared from the customer account and the charges are stopped; or the subscription has been in a delinquent state (FREE_TRIAL_EXPIRED or SUSPENDED) for period of time exceeding the marketplace grace period (typically 2 or 3 months). In this case, the marketplace will notify the ISV by triggering a SUBSCRIPTION_NOTICE event, with a notice type CLOSED, after which he subscription is cleared from the customer account and the charges are stopped.

Subscription Event Notification Flow

All subscription event notifications follow the same general flow:

  1. An event is triggered by a customer action (e.g., purchasing an application, upgrading an existing subscription, cancelling a subscription, or failing to pay an invoice).
  2. AppDirect sends a subscription event notification to the application vendor (ISV). This event is uniquely identified by an event URL.  More details about the notification URL are provided below.
  3. The ISV must validate the OAuth-signature on the request, then can read the event URL from the parameters to send an HTTP GET request for more information about the particular subscription event.
  4. AppDirect sends a JSON or XML response to the ISV.
  5. The ISV then POSTs a request back to AppDirect, passing account and/or status information.
  6. This status information is used by AppDirect to provide feedback to the customer via the AppDirect UI.

 

Synchronous and Asynchronous Events

Typically when AppDirect sends a notification to an ISV, a response is expected. Subscription events are assumed to be synchronous (the response is received immediately) unless otherwise stated. However, when a business process requires a delayed response, AppDirect can support it with an asynchronous event. For asynchronous events, AppDirect listens for the delayed response to arrive.

The SUBSCRIPTION_NOTICE event is the only subscription event that does not involve a response so it is only supported as a synchronous event.

Notification URLs and Responses

When a user triggers an event on an AppDirect-powered marketplace, the marketplace performs an HTTP GET, signed with your OAuth credentials, to your notification URL and passes the event URL as a parameter. You must verify the OAuth signature for the call made to your notification URL is valid and refuse the call by returning an HTTP status 401 or 403 if it is not valid. Once the call has been validated, you can perform an HTTP GET, signed with your OAuth credentials, to the event URL you received to retrieve the information about this event in JSON or XML form. You can then process the event on your side (account creation, account updates, account cancellations, etc.). After processing the event you must respond to the call you received in JSON or XML form indicating the result of the operation. If an error occurred while processing the event, do not return a 500 or 404 status. Always return a 200 status with properly formatted JSON or XML as described below.

Success Response to a Notification


{
    "success": "true",
    "accountIdentifier": "new-account-identifier"
}

  
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<result>
	<success>true</success>
	<accountIdentifier>new-account-identifier</accountIdentifier>
</result>

AppDirect only requires an accountIdentifier in the response to a Subscription Order Event so we can know how to reference the new customer account in future Event Notifications.

Error Response to a Notification


{
	"success": "false",
	"errorCode": "ACCOUNT_NOT_FOUND",
	"message": "The account TEST123 could not be found."
}

  
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<result>
	<success>false</success>
	<errorcode>ACCOUNT_NOT_FOUND</errorcode>
	<message>The account TEST123 could not be found.</message>
<result>

Event Flags

Event notifications may contain an event flag field of the form value. AppDirect supports two possible flag values:

  • DEVELOPMENT: This event was generated for an application that is still in development. This is intended to allow application developers to identify events generated by applications in development that are not publicly visible. For example, it may be used to distinguish debugging and developer accounts from live accounts. These events need to be honored, but will not generate invoices or payments.
  • STATELESS: This event is for testing purposes only. No persistent state change is needed by the application. Applications will be expected to return some well-formed response, such as an error code. This flag will be used for API uptime monitoring.

Example Subscription Event Notification Flow

An example of a subscription event notification flow is shown below using a create subscription.

  1. User Bob purchases application Gizmo123. This creates a SUBSCRIPTION_ORDER event identified by URL https://www.acme-marketplace.com/api/integration/v1/events/12345.
  2. AppDirect calls http://example.com/create?url=https%3A%2F%2Fwww.acme-marketplace.com%2Fapi%2Fintegration%2Fv1%2Fevents%2F12345
  3. Example.com issues a signed fetch to: https://www.acme-marketplace.com/api/integration/v1/events/12345
  4. AppDirect returns a subscription order event.

    
    {
        "type": "SUBSCRIPTION_ORDER",
        "marketplace": {
          "baseUrl": "https://www.acme.com",
          "partner": "APPDIRECT"
        },
        "creator": {
          "address": {
            "firstName": "Sample",
            "fullName": "Sample Tester",
            "lastName": "Tester"
          },
          "email": "sampletester@testco.com",
          "firstName": "Sample",
          "language": "en",
          "lastName": "Tester",
          "locale": "en-US",
          "openId": "https://www.acme.com/openid/id/211aa367-f53b-4606-8887-80a381e0ef69",
          "uuid": "211aa369-f53b-4606-8887-80a361e0ef66"
        },
        "payload": {
          "company": {
            "country": "US",
            "name": "Sample Testing co.",
            "uuid": "bd58b532-323b-4627-a828-57729489b27b",
            "website": "www.testerco.com"
          },
          "order": {
            "editionCode": "FREE",
            "pricingDuration": "MONTHLY"
          }
        }
    }
    
    
      
    <?xml version="1.0" encoding="UTF-8" ?>
    	<event>
    		<type>SUBSCRIPTION_ORDER</type>
    		<marketplace>
    			<baseUrl>https://www.acme.com<baseUrl>
    			<partner>APPDIRECT</partner>
    		</marketplace>
    		<creator>
    			<address>
    				<firstName>Sample</firstName>
    				<fullName>Sample Tester</fullName>
    				<lastName>Tester</lastName>
    			</address>
    			<email>sampletester@testco.com</email>
    			<firstName>Sample</firstName>
    			<language>en</language>
    			<lastName>Tester</lastName>
                            <locale>en-US</locale>
    			<openId>https://www.acme.com/openid/id/211aa367-f53b-4606-8887-80a381e0ef69</openId>
    			<uuid>211aa369-f53b-4606-8887-80a361e0ef66</uuid>
    		</creator>
    		<payload>
    			<company>
    				<country>US</country>
    				<name>Sample Testing co.</name>
    				<uuid>bd58b532-323b-4627-a828-57729489b27b</uuid>
    				<website>www.testerco.com</website>
    			</company>
    			<order>
    				<editionCode>FREE</editionCode>
    				<pricingDuration>MONTHLY</pricingDuration>
    			</order>
    		</payload>
    	</event>
    
    
  5. Example.com creates a new account for Bob in its account system.
  6. Example.com returns aa JSON or XML response to the original HTTP request.

    
    Status Code: 200 OK
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 28
    Content-Type: application/json;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    {
    	"accountIdentifier":"789xyz",
     	"success":true
    }
    
    
      
    Status Code: 200 OK
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 47
    Content-Type: application/xml;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    <result>
    	<accountIdentifier>789xyz</success>
        <success>true</success>
    </result>
    
    

    Or in the event of an error, Example.com can return an error code.

    
    Status Code: 409 Conflict
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 46
    Content-Type: application/json;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    {
     	"success": "false"
    	"errorCode": "USER_ALREADY_EXISTS"
    	"message": "Optional message about the user already existing on ISV"
    }
    
    
      
    Status Code: 409 Conflict
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 62
    Content-Type: application/xml;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    <result>
        <success>false</success>
        <errorCode>USER_ALREADY_EXISTS</errorCode>
        <message>Optional message about the user already existing on ISV</message>
    </result>
    
    

Create Subscription

Customers can purchase products (create a subscription) through the marketplace. These subscriptions can be either FREE_TRIAL (if the edition purchased by the user has a free trial period) or ACTIVE. Subscriptions can be created either through the AppDirect UI (shown below) or by calling the AppDirect subscription API.

The create subscription event notification allows you to receive notice (SUBSCRIPTION_ORDER) when a customer purchases a subscription. Only items which are adjustable by the end user (when purchasing or updating an edition) are included in a create subscription event. Metered billing usage and "included items" are handled separately. In order to allow users to purchase your product, you must define a subcription create notification URL, such as https://example.com/create?eventUrl={eventUrl}, on the product's "Edit Integration" page. AppDirect will call this URL when users purchase new subscriptions.

The general flow for a create subscription event notification is as follows:

  • A user orders an application from AppDirect.
  • AppDirect creates a uniquely identified SUBSCRIPTION_ORDER event, which is persisted in the AppDirect-powered marketplace. This event is uniquely identified by an event URL, such as https://www.acme-marketplace.com/api/integration/v1/events/d15bb36e-5fb5-11e0-8c3c-00262d2cda03.
  • AppDirect calls the configured subscription order notification URL, passing the event URL as a parameter.
  • The application vendor must first validate the OAuth-signature on the request.
  • The application vendor can then get the event URL from the URL parameters and read information regarding the SUBSCRIPTION_ORDER event that was triggered. Briefly, this event contains the following elements:
    • type: The type of the event (SUBSCRIPTION_ORDER in this case).
    • marketplace: Information about the AppDirect-powered marketplace on which the event took place.
    • creator: The identity of the user who triggered the event.
    • payload: The event payload, containing the following elements:
      • order: The order information, which contains an edition code, the quantity of units bought, and the unit type such as "users" associated with the order.
      • company: The company creating the order. Contact information about the ordering company is provided if it is available, but may be empty.
  • The application vendor creates the account and returns a result JSON or XML with these elements:
    • success: Which should be "true" or "false."
    • accountIdentifier: A sequence of characters that will be use to identify the account. This identifier will be used in subsequent transactions to identify this account and cannot be changed.
    • errorCode: If "success" is false, this should contain one of the supported error codes.
    • message: An optional message containing information about the result of the operation.

Example SUBSCRIPTION_ORDER event notification.


{
    "type": "SUBSCRIPTION_ORDER",
    "marketplace": {
      "baseUrl": "https://www.acme.com",
      "partner": "APPDIRECT"
    },
    "creator": {
      "address": {
        "firstName": "Test",
        "fullName": "Test User",
        "lastName": "User"
      },
      "email": "testuser@testco.com",
      "firstName": "Test",
      "language": "en",
      "lastName": " User",
      "locale": "en-US",
      "openId": "https://www.acme.com/openid/id/47cb8f55-1af6-5bfc-9a7d-8061d3aa0c97",
      "uuid": "47cb8f55-1af6-5bfc-9a7d-8061d3aa0c97"
    },
    "payload": {
      "company": {
        "country": "US",
        "name": "tester",
        "phoneNumber": "1-800-333-3333",
        "uuid": "385beb51-51ae-4ffe-8c05-3f35a9f99825",
        "website": "www.testco.com"
      },
      "order": {
        "editionCode": "Standard",
        "pricingDuration": "MONTHLY",
        "items": [{
          "quantity": "4",
          "unit": "USER"
        }]
      }
    }
}

  
<?xml version="1.0" encoding="UTF-8" ?>
<event>
	<type>SUBSCRIPTION_ORDER</type>
	<marketplace>
		<baseUrl>https://www.acme.com</baseUrl>
		<partner>APPDIRECT</partner>
	</marketplace>
	<creator>
		<address>
			<firstName>Test</firstName>
			<fullName>Test User</fullName>
			<lastName>User</lastName>
		</address>
		<email>testuser@testco.com</email>
		<firstName>Test</firstName>>
		<language>en</language>
		<lastName>User</lastName>
                <locale>en-US</locale>
		<openId>https://www.acme.com/openid/id/47cb8f55-1af6-5bfc-9a7d-8061d3aa0c97</openId>
		<uuid>47cb8f55-1af6-5bfc-9a7d-8061d3aa0c97</uuid>
	</creator>
	<payload>
		<company>
			<country>US</country>
			<name>tester</name>
			<phoneNumber>1-800-333-3333</phoneNumber>
			<uuid>385beb51-51ae-4ffe-8c05-3f35a9f99825</uuid>
			<website>www.testco.com</website>
		</company>
		<order>
			<editionCode>Standard</editionCode>
			<pricingDuration>MONTHLY</pricingDuration>
			<items>
				<quantity>4</quantity>
				<unit>USER</unit>
			</items>
		<order>
	</payload>
</event>

Change Subscription

Existing subscriptions can be updated either through the AppDirect UI or by calling the AppDirect subscription API.

The change subscription event notification allows you to receive notice (SUBSCRIPTION_CHANGE) when an existing subscription is changed in some way (e.g,. upgrade or downgrade).  A SUBSCRIPTION_CHANGE is triggered whenever an admin within the AppDirect platform modifies the current subscription, which would be represented by a change to the plan values or plan ID.  It can also be triggered by the admin transferring the owner of the subscription, which would be represented with new creator values. Only items which are adjustable by the end user (when purchasing or updating an edition) are included in a change subscription event. Metered billing usage and "included items" are handled separately. In order to allow users to make changes to subscriptions, you must configure a Subscription Change Notification URL, such as https://example.com/change?eventUrl={eventUrl}, in the product "Edit Integration

The general flow for a change subscription event notification is as follows:

  • A user changes (upgrades or downgrades) a subscription from AppDirect.
  • AppDirect creates a uniquely identified SUBSCRIPTION_CHANGE event, which is persisted in the AppDirect-powered marketplace. This event is uniquely identified by an event URL, such as https://www.acme-marketplace.com/api/integration/v1/events/d15bb36e-5fb5-11e0-8c3c-00262d2cda03.
  • AppDirect calls the configured subscription change notification URL, passing the event URL as a parameter.
  • The application vendor must first validate the OAuth-signature on the request.
  • The application vendor can then get the event URL from the URL parameters and read information regarding the SUBSCRIPTION_CHANGE that was triggered. Briefly, this event contains the following elements:
    • type: The type of the event (SUBSCRIPTION_CHANGE in this case)
    • marketplace: Information about the AppDirect-powered marketplace on which the event took place.
    • creator: The identity of the admin creating the event.
    • payload: The event payload, containing the following elements:
      • order: The order change information.
      • account: The account identifier provided by the Application Vendor in the initial Subscription Order Event.
  • The application vendor returns a result JSON or XML with these elements:
    • success: Which should be "true" or "false."
    • errorCode: If "success" is false, this should contain one of the supported error codes.
    • message: An optional message containing information about the result of the operation.

Example SUBSCRIPTION_CHANGE event notification.


{
    "type": "SUBSCRIPTION_CHANGE",
    "marketplace": {
      "baseUrl": "https://www.acme.com",
      "partner": "APPDIRECT"
    },
    "creator": {
      "address": {
        "city": "San Jose",
        "country": "US",
        "firstName": "Test",
        "fullName": "Test User",
        "lastName": "User",
        "state": "CA",
        "street1": "1 Main St",
        "zip": "95131"
      },
      "email": "testuser@testco.com",
      "firstName": "Test",
      "language": "en",
      "lastName": "User",
      "locale": "en-US",
      "openId": "https://www.acme.com/openid/id/7f59aad1-85cd-4c04-b35b-906ee53acc71",
      "uuid": "7f59aad1-85cd-4c04-b35b-906ee53acc71"
    },
    "payload": {
      "account": {
        "accountIdentifier": "206123",
        "status": "ACTIVE"
      },
      "order": {
        "editionCode": "DME",
        "pricingDuration": "DAILY",
        "items": [{
          "quantity": "0",
          "unit": "GIGABYTE"
        }]
      }
    }
}

  
<?xml version="1.0" encoding="UTF-8" ?>
<event>
	<type>SUBSCRIPTION_CHANGE</type>
	<marketplace>
		<baseUrl>https://www.acme.com</baseUrl>
		<partner>APPDIRECT</partner>
  	</marketplace>
	<creator>
		<address>
			<city>San Jose</city>
			<country>US</country>
			<firstName>Test</firstName>
			<fullName>Test User</fullName>
			<lastName>User</lastName>
			<state>CA</state>
			<street1>1 Main St</street1>
			<zip>95131</zip>
		</address>
		<email>testuser@testco.com</email>
		<firstName>Test</firstName>
		<language>en</language>
		<lastName>User</lastName>
                <locale>en-US</locale>
		<openId>https://www.acme.com/openid/id/7f59aad1-85cd-4c04-b35b-906ee53acc71</openId>
		<uuid>7f59aad1-85  "event": {cd-4c04-b35b-906ee53acc71</uuid>
	</creator>
	<payload>
		<account>
			<accountIdentifier>206123</accountIdentifier>
			<status>ACTIVE</status>
		</account>
		<order>
			<editionCode>DME</editionCode>
			<pricingDuration>DAILY</pricingDuration>
			<items>
				<quantity>0</quantity>
				<unit>GIGABYTE</unit>
			</items>
		</order>
	</payload>
</event>


Cancel Subscription

Existing subscriptions can be cancelled either through the AppDirect UI or by calling the AppDirect subscription API.

The cancel subscription event notification allows you to receive notice (SUBSCRIPTION_CANCEL) when the customer cancels an existing subscription. In order to allow users to cancel their subscription to your product, you must define a subcription cancel notification URL, such as https://example.com/cancel?eventUrl={eventUrl}, on the product's "Edit Integration" page.

If AppDirect does not receive a successful response from the ISV to a SUBSCRIPTION_CANCEL event, the cancellation process is aborted, and the subscription stays in its current state. The customer is notified of the failure and presented with the error message returned by the ISV (if any).

The general flow for a cancel subscription event notification is as follows:

  • A user cancels a subscription from AppDirect.
  • AppDirect creates a SUBSCRIPTION_CANCEL event identified by a corresponding token value.
  • AppDirect calls the Subscription Cancel Notification URL.
  • The application vendor must validate AppDirect's OAuth-signature.
  • The application vendor issues an OAuth-signed fetch to read the SUBSCRIPTION_CANCEL Event information using the token value. See the Event API documentation for details. Briefly, this subscription cancel event contains:
    • type: The type of the event (SUBSCRIPTION_CANCEL in this case)
    • marketplace: Information about the AppDirect-powered marketplace on which the event took place.
    • creator: The identity of the admin creating the event.
    • payload: The event payload, containing the following elements:
      • account: The account identifier provided by the Application Vendor in the initial Subscription Order Event.
  • The application vendor returns a result JSON or XML with these parameters:
    • success: Which should be "true" or "false."
    • errorCode: If "success" is false, this should contain one of the supported error codes.
    • message: An optional message containing information about the result of the operation.

Example SUBSCRIPTION_CANCEL event notification.


{
    "type": "SUBSCRIPTION_CANCEL",
    "marketplace": {
      "baseUrl": "https://www.acme.com",
      "partner": "APPDIRECT"
    },
    "creator": {
      "address": {
        "city": "Sommerville",
        "country": "US",
        "firstName": "Test",
        "fullName": "Test User",
        "lastName": "User",
        "phone": "5305556465",
        "state": "MA",
        "street1": "55 Grove St",
        "zip": "02144"
      },
      "email": "testuser@testco.com",
      "firstName": "Test",
      "language": "en",
      "lastName": "User",
      "locale": "en-US",
      "openId": "https://www.acme.com/openid/id/d124bf8b-0b0b-40d3-831b-b7f5a514d487",
      "uuid": "d124bf8b-0b0b-40d3-831b-b7f5a514d487"
    },
    "payload": {
      "account": {
        "accountIdentifier": "9d6fca98-aa94-462b-85fa-118804ad3fe3",
        "status": "ACTIVE"
      }
    }
}

  
<?xml version="1.0" encoding="UTF-8" ?>
<event>
	<type>SUBSCRIPTION_CANCEL</type>
	<marketplace>
		<baseUrl>https://www.acme.com</baseUrl>
		<partner>APPDIRECT</partner>>
	</marketplace>
	<creator>
		<address>
			<city>Sommerville</city>
			<country>US</country>
			<firstName>Test</firstName>
			<fullName>Test User</fullName>
			<lastName>User</lastName>
			<phone>5305556465</phone>
			<state>MA</state>
			<street1>55 Grove St</street1>
			<zip>02144</zip>
		</address>
		<email>testuser@testco.com</email>
		<firstName>Test</firstName>
		<language>en</language>
		<lastName>User</lastName>
                <locale>en-US</locale>
		<openId>https://www.acme.com/openid/id/d124bf8b-0b0b-40d3-831b-b7f5a514d487</openId>
		<uuid>d124bf8b-0b0b-40d3-831b-b7f5a514d487</uuid>
	</creator>
	<payload>
		<account>
			<accountIdentifier>9d6fca98-aa94-462b-85fa-118804ad3fe3</accountIdentifier>
			<status>ACTIVE</status>
		</account>
	</payload>
</event>

Subscription Notice

The subscription notice event notification allows you to receive overdue or deliquent notices on an existing subscription, as well as to receive notice (SUBSCRIPTION_NOTICE) when the overdue invoice has been resolved. In order to support these subscription notices, you must define a subcription notice notification URL on the product's "Edit Integration" page.

The general flow for a subscription notice event notification is as follows:

  • A subscription's status changes, such as going overdue or delinquent, on AppDirect.
  • AppDirect creates a SUBSCRIPTION_NOTICE event identified by an event URL.
  • AppDirect performs a GET operation on the URL defined as the Subscription Notice listener URL, passing the event URL identifying this event.
  • The application vendor issues an OAuth-signed fetch to read the SUBSCRIPTION_NOTICE Event information using the token value. See the Event API documentation for details. Briefly, this Subscription Notice Event contains:
    • type: The type of the event (SUBSCRIPTION_NOTICE in this case)
    • marketplace: Information about the AppDirect-powered marketplace on which the event took place.
    • payload: The event payload, containing the following elements:
      • notice: A type element whose value can be either DEACTIVATED, REACTIVATED, CLOSED or UPCOMING_INVOICE.
      • account: The account identifier provided by the application in the initial Subscription Order Event and its current status (FREE_TRIAL, SUSPENDED, etc.).
  • The application vendor returns a result JSON or XML with these parameters:
    • success: Which should be "true" or "false."
    • errorCode: If "success" is false, this should contain one of the supported error codes.
    • message: An optional message containing information about the result of the operation.

Example SUBSCRIPTION_NOTICE event notification.


{
    "type": "SUBSCRIPTION_NOTICE",
    "marketplace": {
      "baseUrl": "https://www.acme.com",
      "partner": "APPDIRECT"
    },
    "payload": {
      "account": {
        "accountIdentifier": "a3f72246-5377-4d92-8bdc-b1b6b450c55c",
        "status": "ACTIVE"
      },
      "notice": { "type": "UPCOMING_INVOICE" }
    }
}

  
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<event>
	<type>SUBSCRIPTION_NOTICE</type>
	<marketplace>
		<baseurl>https://www.acme.com</baseurl>
		<partner>APPDIRECT</partner>
	</marketplace>
        <payload>
		<account>
			<accountidentifier>a3f72246-5377-4d92-8bdc-b1b6b450c55c</accountidentifier>
			<status>ACTIVE</status>
		</account>
		<configuration/>
		<notice>
			<type>UPCOMING_INVOICE</type>
		</notice>
  	</payload>
</event>

Notice Types

Notice Type Description

DEACTIVATED

A DEACTIVATED notice means the account has been deactivated and is in a SUSPENDED or FREE_TRIAL_EXPIRED state. It is recommended that all access to the ISV's application be suspended (but not deleted). Account deactivation may occur if, for example, the account holder is overdue in making a payment or if abuse is detected.

REACTIVATED

A REACTIVATED notice means an account should be considered active (ACTIVE or FREE_TRIAL state) and receive its typical access. This status will usually indicate that the account holder has paid an overdue invoice.

CLOSED

A CLOSED notice means that the account is in a CANCELLED, and that it should be deleted by the ISV. In most cases, this event should trigger the same code on the ISV as the SUBSCRIPTION_CANCELLED event.

UPCOMING_INVOICE

An UPCOMING_INVOICE notice informs a vendor that there is an upcoming invoice that will be computed for this account, before it is generated. This notice is issued 21 hours prior to the start of the invoice run that will include the vendor's invoice. (If an invoice is queued and generated, for example, four hours after the invoice generation run starts, the upcoming invoice notice is issued 25 hours before invoice generation.) The intention of this notice is to give a vendor the opportunity to update the AppDirect-powered marketplace with any usage information via the Billing Usage API, which will be included on the upcoming invoice.

Error Handling

Since the DEACTIVATED, REACTIVATED and CLOSED notices reflect state changes in the subscription lifecycle, it is important for ISVs to process these events successfully. To mitigate temporary errors (downtimes, transport errors, etc.), the AppDirect marketplace will attempt to redeliver the notice up to 10 times, following an exponential backoff algorithm: the first redelivery will be attempted 30 minutes after the last failure, then 1 hour after the last failure, then 2 hours, etc. to a minimum of once a day. The redelivery will happen in a 1 hour window following the indicated time (so the first redelivery can happen anytime from 30 minutes to 1 hour 30 minutes after the initial failure).

If the marketplace does not receive a successful response from the ISV to a SUBSCRIPTION_NOTICE event, the behavior is as follows:

  • for UPCOMING_INVOICE notices, the invoice is still sent out to the customer. The ISV will be notified again when the next invoice is about to be created.
  • for DEACTIVATED, REACTIVATED and CLOSED notices, the marketplace willl attempt to redeliver the notice up to 10 times according to an exponential backoff algorithm. The redelivery attempt will occur 30min, 1h, 2h, 4h, etc. up to a day after the time of last failure to deliver.

Asynchronous Events

All subscription events that involve a response can also be supported as asynchronous (delayed response) events. (SUBSCRIPTION_NOTICE is the only subscription event that does not involve a response and therefore is only supported as a synchronous event.)

When AppDirect calls an ISV with a notification URL, they return a JSON or XML response, as well as an HTTP response code. Usually, this code is 200 (OK) or 201 (Created). Additionally, however, AppDirect will recognize an code of 202 (Accepted) as a mechanism for the ISV to tell AppDirect that the event has been received and is being processed, and the ISV will notify AppDirect upon completion. The ISV can make an HTTP POST to the /api/integration/v1/events/{eventUrl}/result endpoint to notify AppDirect of the result. The POST body is the same as it would be for a synchronous event response.  The subscription or assignment will remain in a "pending" state (e.g., PENDING_USER_ACTIVATION, PENDING_REMOTE_CREATION, or PENDING_REMOTE_CANCELLATION) in the AppDirect marketplace until AppDirect is notified of the result. When a subscription or assignment is in the pending state, the user cannot act upon it.

Asynchronous Event Notification Flow

All asynchronous event notifications follow the same general flow:

  1. An event is triggered by a customer action (e.g., purchasing an application, upgrading an existing subscription, cancelling a subscription, or failing to pay an invoice).
  2. AppDirect sends an asynchronous event notification to the application vendor (ISV). This event is uniquely identified by an event URL.  More details about the notification URL are provided below.
  3. The ISV must validate the OAuth-signature on the request, then can read the event URL from the parameters to send an HTTP GET request for more information about the particular subscription event.
  4. AppDirect sends a JSON or XML response to the ISV.
  5. The ISV then does a POST request back to AppDirect, passing account and/or status information. This status information is used by AppDirect to provide feedback to the customer via the AppDirect UI.
  6. The key difference with an asynchronous event is that the header returns a 202 (Accepted) HTTP code. AppDirect will recognize this code as a pending event.
  7. Once the event is manually resolved (such as an account is provisioned), the ISV can POST a result to the eventUrl.

 

Example Asynchronous Event Notification Flow

An example of a asynchronous event notification flow is shown below using a create subscription.

  1. User Bob purchases application Gizmo123. This creates an asynchronous SUBSCRIPTION_ORDER event identified by URL https://www.acme-marketplace.com/api/integration/v1/events/12345.
  2. AppDirect calls http://example.com/create?url=https%3A%2F%2Fwww.acme-marketplace.com%2Fapi%2Fintegration%2Fv1%2Fevents%2F12345
  3. Example.com issues a signed fetch to: https://www.acme-marketplace.com/api/integration/v1/events/12345
  4. AppDirect sends a JSON or XML response to the ISV.

    
    {
        "type": "SUBSCRIPTION_ORDER",
        "marketplace": {
          "baseUrl": "https://www.acme.com",
          "partner": "APPDIRECT"
        },
        "creator": {
          "address": {
            "city": "Cambridge",
            "country": "US",
            "phone": "8582312882",
            "state": "MA",
            "street1": "123 Main St",
            "zip": "02142"
          },
          "email": "testuser@testco.com",
          "firstName": "Test",
          "language": "en",
          "lastName": "User",
          "locale": "en-US",
          "openId": "https://www.acme.com/openid/id/5d1f6f79-efff-411e-abe5-0b0a01610f04",
          "uuid": "5d1f6f79-efff-411e-abe6-0b0a01610f04"test
        },
        "payload": {
          "company": {
            "country": "US",
            "name": "Test User",
            "phoneNumber": "8582312882",
            "uuid": "dc61a736-55b6-40fc-9b5a-6b17cbe6eb62"
          },
          "order": {
            "editionCode": "0D5C06DB-FFEC-43a1-A6AF-EFB7E9B17905",
            "pricingDuration": "MONTHLY",
            "items": [{
              "quantity": "3",
              "unit": "USER"
            }]
          }
        }
    }
    
    
      
    <?xml version="1.0" encoding="UTF-8" ?>
    <event>
            <type>SUBSCRIPTION_ORDER</type>
            <marketplace>
                    <baseUrl>https://www.acme.com</baseUrl>
                    <partner>APPDIRECT</partner>
            </marketplace>
            <creator>
                    <address>
                            <city>Cambridge</city>
                            <country>US</country>
                            <phone>8582312882</phone>
                            <state>MA</state>
                            <street1>123 Main St</street1>
                            <zip>02142</zip>
                    </address>
                    <email>testuser@testco.com</email>
                    <firstName>Test</firstName>
                    <language>en</language>
                    <lastName>User</lastName>
                    <locale>en-US</locale>
                    <openId>https://www.acme.com/openid/id/5d1f6f79-efff-411e-abe5-0b0a01610f04</openId>
                    <uuid>5d1f6f79-efff-411e-abe6-0b0a01610f04</uuid>
            </creator>
            <payload>
                    <company>
                            <country>US</country>
                            <name>Test User</name>
                            <phoneNumber>8582312882</phoneNumber>
                            <uuid>dc61a736-55b6-40fc-9b5a-6b17cbe6eb62</uuid>
                    </company>
                    <order>
                            <editionCode>0D5C06DB-FFEC-43a1-A6AF-EFB7E9B17905</editionCode>
                            <pricingDuration>MONTHLY</pricingDuration>
                            <items>
                                    <quantity>3</quantity>
                                    <unit>USER</unit>
                            </items>
                    </order>
            </payload>
    </event>
    
    
    
  5. Example.com starts the creation a new account for Bob in its account system.
  6. Example.com returns a JSON or XML response to the original HTTP request.

    
    Status Code: 202 Accepted
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 17
    Content-Type: application/json;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    {
     	"success":true
    }
    
    
      
    Status Code: 202 Accepted
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 35
    Content-Type: application/xml;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    <result>
        <success>true</success>
    </result>
    
    
  7. AppDirect puts the subscription into the PENDING_REMOTE_CREATION state. The user cannot do anything with the subscription at this time.
  8. The ISV sends an HTTP POST to https://www.acme-marketplace.com/api/integration/v1/events/12345/result to indicate the account creation is complete. Note that the POST body is the same object as a synchronous event response. 
    
    Status Code: 200 OK
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 28
    Content-Type: application/json;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    {
    	"accountIdentifier":"789xyz",
     	"success":true
    }
    
    
      
    Status Code: 200 OK
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 47
    Content-Type: application/xml;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    <result>
    	<accountIdentifier>789xyz</success>
        <success>true</success>
    </result>
    
    

    Or in the event of an error, Example.com can return an error code.

    
    Status Code: 409 Conflict
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 46
    Content-Type: application/json;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    {
     	"success": "false"
    	"errorCode": "USER_ALREADY_EXISTS"
    	"message": "Optional message about the user already existing on ISV"
    }
    
    
      
    Status Code: 409 Conflict
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Content-Length: 62
    Content-Type: application/xml;charset=UTF-8
    Date: Tue, 25 Aug 2015 20:40:25 GMT
    Expires: 0
    Pragma: no-cache
    Server: Apache-Coyote/1.1
    Set-Cookie: JSESSIONID=E93047B4DE01A6D3447EACEDD462CA8A; Path=/; HttpOnly
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    x-content-type-options: nosniff
    
    <result>
        <success>false</success>
        <errorCode>USER_ALREADY_EXISTS</errorCode>
        <message>Optional message about the user already existing on ISV</message>
    </result>
    
    

Attributes

AppDirect may pass several attributes as part of the request (JSON or XML) when it sends a event notification request to an ISV. Below is a table summarizing all of the possible attributes, as well as a table summarizing the attributes expected in the response (JSON or XML).

Supported Request Attributes

Request Field Format Required Description

type

String Enum

Yes

The type of event triggered in the AppDirect marketplace. Can be SUBSCRIPTION_ORDER, SUBSCRIPTION_CHANGE, SUBSCRIPTION_CANCEL, SUBSCRIPTION_NOTICE, USER_ASSIGNMENT, USER_UNASSIGNMENT, or USER_UPDATED.

marketplace

Object

Yes

Information about the partner channel.

marketplace.partner

String

Yes

The name of the channel, defaults to APPDIRECT.

marketplace.baseUrl

String

Yes

The root URL for the partner channel.

applicationUuid

String

No

The unique identifier for the application.

flag

String Enum

No

Flag indicating how the event is used, may be STATELESS or DEVELOPMENT.

returnUrl

String

No

An optional redirect URL.

creator

Object

No

The identity of the admin creating the event. Note that this will be the original purchaser of the application for SUBSCRIPTION_ORDER, but may be other users for the other event types.

creator.email

String

Yes

Email address for the creator.

creator.firstName

String

No

First name of the creator.

creator.language

String

No

Default language for the creator.

creator.lastName

Object

No

Last name of the creator.

creator.locale

String

No

Default locale for the creator.

creator.openId

String

No

The openId for the creator.

creator.uuid

String

No

Unique user identifier for the creator.

creator.address

Object

No

The creator's address

address.city

String

Yes, if address specified

Creator address city

address.country

String

Yes, if address specified

Creator address country

address.state

String

No

Creator address state

address.street1

String

Yes, if address specified

Creator street address line 1

address.street2

String

No

Creator street address line 2

address.zip

String

Yes, if address specified

Creator address zip code

creator.attributes

Array

No

Optional attributes about the creator.

payload

 

payload.user

Object

No

The affected user; may or may not be the same as the creator.

user.email

String

Yes

Email address for the user.

user.firstName

String

No

First name of the user.

user.language

String

No

Default language for the user.

user.lastName

Object

No

Last name of the user.

user.locale

String

No

Default locale for the user.

user.openId

String

No

The openId for the user.

user.uuid

String

No

Unique user identifier for the user.

user.attributes

Array

No

Optional attributes about the user.

payload.user.address

Object

No

The user's address

address.city

String

Yes, if address specified

User address city

address.country

String

Yes, if address specified

User address country

address.state

String

No

User address state

address.street1

String

Yes, if address specified

User street address line 1

address.street2

String

No

User street address line 2

address.zip

String

Yes, if address specified

User address zip code

payload.account

Object

Yes, if account has been created.

The account associated with the event, except for SUBSCRIPTION_ORDER (when account is created).

accountIdentifier

String

Yes, if account has been created.

The unique account identifier.

payload.account.parentAccountIdentifier

String

Yes, when changing an add-on (version 2).

The account identifier for the main application subscription associated with this add-on (version 2). (Available once account is created.)

status

String

Yes, if account has been created.

The current account status. Can be INITIALIZED, FAILED, FREE_TRIAL, FREE_TRIAL_EXPIRED, ACTIVE, SUSPENDED, or CANCELLED. The account will be in the INITIALIZED state until the ISV provides a result back to AppDirect indicating whether the account was successfully created or not.  When success is true, for instance, the account will be put into an ACTIVE or FREE_TRIAL state as appropriate.  When success is false, the account will be put into a FAILED state.

payload.company

Object

Yes, for SUBSCRIPTION_ORDER

Information about the company associated with the event.

company.uuid

String

Yes

The unique identifier for the company.

company.externalId

String

No

The external identifier for the company.

company.name

String

Yes

The name of the company.

company.email

String

Yes

The email associated with the company.

company.phoneNumber

String

Yes

The phone number associated with the company.

company.website

String

Yes

The company's website.

company.country

String

No

The company's country.

payload.order

Object

Yes, for SUBSCRIPTION_ORDER and SUBSCRIPTION_CHANGE

Part of payload, SUBSCRIPTION_ORDER and SUBSCRIPTION_CHANGE

order.editionCode

String

Yes

The code for the edition associated with the subscription event.

order.addonOfferingCode

String

No

The code for the addon offering associated with the subscription event, if there is an addon.

order.pricingDuration

String

Yes

The pricing duration for the edition associated with the subscription event.

order.items

Array

Yes

The line items comprising the order associated with the subscription event.

payload.addonInstance

Object

Yes, for addon-related events

Information about the addon.

addonInstance.id

String

Yes

The identifier for the addon instance.

payload.addonBinding

Object

Yes, for addon-related events

Information about the addon binding.

addonInstance.id

String

Yes

The identifier for the addon binding.

payload.notice

Object

Yes, for SUBSCRIPTION_NOTICE only.

Information about the notice.

notice.type

String Enum

Yes

Type of notice; can be REACTIVATED, DEACTIVATED, or CLOSED

notice.message

String

No

A message containing additional information about the notice.

payload.configuration

Array

Yes, for SUBSCRIPTION_ORDER and SUBSCRIPTION_CHANGE

The configuration from the subscription's purchase order.

Response Attributes

Response Parameter Description

accountIdentifier

Unique account identifier for the customer on the ISV. Only required in the response to a Subscription Order Event.

userIdentifier

An optional unique user identifier for the user on the ISV.

errorCode

An error code as described below required when success is false.

message

An optional message string to use in conjunction with the errorCode.

success

A required boolean value -- true when success, false when error.

Error Codes

Occasionally, it is necessary to return an error. If the application vendor must communicate a failure when processing information from AppDirect, one of the following error codes must be returned:

Error Code Description

USER_ALREADY_EXISTS

This error code is typically used when AppDirect admins try to buy subscriptions for apps they have already purchased directly from the application vendor. In this scenario, we will show the user an error message and prompt them to link their account.

USER_NOT_FOUND

This error code is typically used when AppDirect admins try to unassign users not found in the application vendor's account.

ACCOUNT_NOT_FOUND

This error code is typically used when AppDirect admins try to add or remove users from an account not found in the application vendor's records.

MAX_USERS_REACHED

This error code is typically used when AppDirect admins try to assign users beyond the limit of the number of seats available. AppDirect will typically prevent that from happening by monitoring application usage.

UNAUTHORIZED

This error code is returned when users try any action that is not authorized for that particular application, for example, if an application does not allow the original creator to be unassigned.

OPERATION_CANCELED

This error code is returned when a user manually interrupts the operation (clicking cancel on the account creation page, etc.).

CONFIGURATION_ERROR

This error code is returned when the vendor endpoint is not currently configured.

INVALID_RESPONSE

This error code is returned when the vendor was unable to process the event fetched from AppDirect.

PENDING

This error code is returned when the vendor was unable to process the event because the service is under provisioning.

FORBIDDEN

This error code is returned when the operation is not allowed.

BINDING_NOT_FOUND

This error code is returned when the specified binding cannot be found.

TRANSPORT_ERROR

This error code is returned when the there is a transport error, such as the server being unreachable.

UNKNOWN_ERROR

This error code may be used when none of the other error codes apply.