Develop an Oracle Eloqua app content service

Content allows marketers to source pieces of content in Eloqua emails and landing pages, from an external source. This is the new and improved version of Eloqua's cloud components framework, and it includes many improvements such as asynchronous bulk processing (for emails), the ability to fully test the content service within Eloqua, and design-time interaction with the email and landing page editors.

Content follows the instantiation-execution model.The following sections describe content-specific details and provide the endpoints needed to develop a content service. If you are not familiar with the general flow for the instantiation-execution model, you should read that first.

Eloqua signs all outgoing calls with OAuth 1.0a so the receiving system can validate that the call was sent by Eloqua. Refer to the OAuth 1.0a spec or OAuth 1.0 RFC for more information.

Note: If you prefer a more visual representation of the flow for content services, check out the following flow diagrams:

Create URL

Eloqua's call to the CreateURL:

POST https://example.com/awesomeapp/content/create?instance=f82d50cd-86a9-4fca-b37e-4ec9a98b0339

{}

For a content service, AwesomeApp's response must include height, width, and default editorImageUrl, as well as the recordDefinition parameter. You can include a maximum of 250 fields in your record definition. For more details, see: bulk API limits.

The height and width parameters define the size of the content instance when rendered, while editorImageUrl specifies the URL for an image that Eloqua will display in the editor's design surface. editorImageUrl is not a templated URL.

Note: The "Content-Type" header must be set to "application/json" for the response to the Create URL call.

HTTP/1.1 200 application/json; charset=utf-8

{
  "recordDefinition": {
    "ContactID": "{{Contact.Id}}"
  },
  "height": 256,
  "width": 256,
  "editorImageUrl": "https://example.com/image.png",
  "requiresConfiguration": true
}

requiresConfiguration is an optional Boolean parameter which tells Eloqua whether user configuration is required before the app can be used. If set to true, users will be unable to save an email or landing page asset containing the unconfigured app service instance. Eloqua will display an error message.

Warning: All field names and values in Eloqua data transfer objects are case sensitive. Be sure to reproduce them exactly. For example: {{Contact.id}} would fail if the correct form is {{Contact.Id}}.

Configure URL

When a user clicks the edit button on the service instance and makes changes through the Configure URL that require an updated recordDefinition DTO, AwesomeApp must call out to Eloqua’s cloud API PUT /contents/instances/{id} endpoint with that updated DTO:

PUT https://secure.eloqua.com/api/cloud/1.0/contents/instances/f82d50cd-86a9-4fca-b37e-4ec9a98b0339
{    
"recordDefinition": {
"ContactID": "{{Contact.Id}}",
"EmailAddress": "{{Contact.Field(C_EmailAddress)}}",
},
"height": 256,
"width": 256,
"editorImageUrl": "https://example.com/image.png", "requiresConfiguration": false
}

requiresConfiguration is an optional Boolean parameter which tells Eloqua whether user configuration is required before the app can be used. If set to true, users will be unable to save an email or landing page asset containing the unconfigured app service instance. Eloqua will display an error message.

Use the configure URL response to set requiresConfiguration to false when your app's configuration acceptance criteria have been met.

Note: X-Frame-Options must not be set to DENY for any configure page.

Warning: If the email or landing page asset is not in draft mode, attempting to set requiresConfiguration to true will result in an error.

Notification URL

Eloqua's call to the Notification URL transmits the requested fields in the items parameter:

POST https://example.com/awesomeapp/content/notify?instance=f82d50cd-86a9-4fca-b37e-4ec9a98b0339&asset=456
{
"offset": 0,
"limit": 1000,
"totalResults": 2,
"count": 2,
"hasMore": false,
"items":
[
{
"ContactID": "1",
"EmailAddress": "fred@example.com"
},
{
"ContactID": "2",
"EmailAddress": "john@example.com"
}
]
}

For content services, AwesomeApp's response will depend on whether the content is for an email or a landing page:

For landing pages, the response is a 200 status code with text/html content inline. If the response takes too long, Eloqua uses the default content for that contact.

For Email, AwesomeApp can respond in one of two ways:

  • Inline response: A 200 status code with text/html content inline, in which case Eloqua uses the same content for each contact (this is the same response type as used for landing pages), or
  • Asynchronous response: Asynchronous response: A 204 status code, indicating that the call was accepted, but there is no content to return directly. The service should process asynchronously and then call the bulk API. Eloqua waits 24 hours for a response. If there is no response from the app after 24 hours the email is not sent and those records are set to error.

Important: If there is no response or an error returned for the Notification Call Eloqua uses the default content for that contact.

Inline response

The inline response for landing pages and email is a 200 status code, followed by the text/html content. For example:

{
<div>
<h1>External Content</h1>
<p>This content is used for all recipients for this request.</p>
</div>
}

Asynchronous response

For the asynchronous response, AwesomeApp responds with a 204 status code, indicating that the response will be asynchronous, followed by one or more imports to Eloqua's bulk API, where contact-specific content is updated with the ContactId and the instance Id, mapping the contact to the new html content.

Note: The maximum import size per batch is 5,000. If you have more than 5,000 contacts, break your data up into multiple imports.

  1. Create the bulk import definition, including a Content parameter whose value is the service instance ID and execution ID. Including the execution ID enables you to differentiate between different uses of the asset, for example, when multiple campaigns use the same email template that contains the service instance:
    POST https://secure.eloqua.com/api/bulk/2.0/contacts/imports 
    {
    "name": "AwesomeApp Content Response Bulk Import",
    "updateRule": "always",
    "fields": {
    "EmailAddress": "{{Contact.Field(C_EmailAddress)}}",
    "Content": "{{ContentInstance(f82d50cd86a94fcab37e4ec9a98b0339).Execution[21]}}"
    },
    "identifierFieldName": "EmailAddress"
    }

    Eloqua's response will be a 201 Created response that includes a uri parameter, which you can use to identify the import:

    HTTP/1.1 201 Created
    {
    "name" : "AwesomeApp Content Response Bulk Import",
    "updateRule" : "always",
    "fields" : {
    "EmailAddress" : "{{Contact.Field(C_EmailAddress)}}",
    "Content" : "{{ContentInstance(f82d50cd86a94fcab37e4ec9a98b0339).Execution[21]}}"
    },
    "identifierFieldName" : "EmailAddress",
    "isSyncTriggeredOnImport" : false,
    "isUpdatingMultipleMatchedRecords" : false,
    "uri" : "/contacts/imports/6",
    "createdBy" : "DocsExample",
    "createdAt" : "2014-03-06T13:59:00.6600046Z",
    "updatedBy" : "DocsExample",
    "updatedAt" : "2014-03-06T13:59:00.6600046Z"
    }
  2. Send Eloqua the data for import, using the content parameter with the content the Eloqua should use for each contact:
    POST https://secure.eloqua.com/api/bulk/2.0/contacts/imports/6/data
    [
    {
    "EmailAddress" : "fred@example.com",
    "Content" : "<p>This is the content for Fred</p>"
    },
    {
    "EmailAddress" : "sylvie@example.com",
    "Content" : "<p>This is the content for Sylvie</p>"
    }
    ]
  3. Synchronize the data for import:

    AwesomeApp's request:

    POST https://secure.p03.eloqua.com/api/bulk/2.0/syncs
    {
    "syncedInstanceURI":"/contacts/imports/6"
    }

    Eloqua's response:

    201 Created

    {
    "syncedInstanceURI" : "/contacts/imports/6",
    "status" : "pending",
    "createdAt" : "2014-01-01T13:59:07.1375620Z",
    "createdBy" : "DocsExample",
    "uri" : "/syncs/6"
    }
  4. You can then use the sync's URI (/syncs/6) to check the status of the sync:

    GET https://secure.p03.eloqua.com/api/bulk/2.0/sync/6

    For a full description of the sync status codes, see: bulk API sync status codes

    When the sync is complete, Eloqua's response will resemble:

    200 OK

    {
    "syncedInstanceURI" : "/contacts/imports/6",
    "syncStartedAt" : "2014-01-01T13:59:07.1375620Z",
    "syncEndedAt" : "2014-01-01T13:59:14.1375620Z",
    "status" : "success",
    "createdAt" : "2014-01-01T13:59:07.1375620Z",
    "createdBy" : "DocsExample",
    "uri" : "/syncs/6"
    }

If you run into trouble or have questions about the bulk API, check out the bulk API documentation.

Notes:
  • If you want your content service hyperlinks to take advantage of Eloqua's dynamic link tracking features, it will not do so automatically. To enable Eloqua tracking for a link within your content service:

    • If the destination page has an Eloqua tracking script installed you should append
      s=<span class=eloquaemail>siteid</span>&elq=<span class=eloquaemail>recipientid</span> to the URL.
    • If the destination page does not have an Eloqua tracking script installed you should append
      elqtrack=true to the URL.
  • The http protocol must be included to use Eloqua tracking parameters. For example:

    • google.com?elqTrack=true - Detected as invalid URL. No clickthrough is recorded.
    • http://google.com?elqTrack=true - Detected as valid URL. Clickthrough is recorded.

Delete URL

The delete URL is a templated URL pointing to an endpoint for deleting an instance of your service.

The delete URL uses an HTTP DELETE request and there is no content sent in the request body. All common URL template parameters are available (the same as with a create URL). On success, this endpoint should return a 200-level response.

An example delete URL would look something like:

https://www.someurl.com/delete/{appId}/{installId}/{instanceId}/{userName}/{siteName}/{siteId}

Note: Delete calls are not sent in real time, but are done in a batch once daily.

Learn more

Service descriptions

Service level URL template parameters

App developer reference