Document Generation Using Apex Call
This topic describes the details and sample code to merge the document with Salesforce data using Apex call. You might use this functionality differently to generate documents depending on your business case.
See Composer REST APIs for a comprehensive list of Composer APIs that you can use for your business use case.
Prerequisites
The Composer APIs are available as part of an early adopter program that requires Conga product management review for inclusion.
- Contact your Account Executive with your 18-digit OrgID to enable the Composer API feature.
- Refer to the Authentication section to generate the Client ID and Client Secret that will be used in the code to connect to the Conga Authorization server.
- Refer to Step 2 to obtain the sessionId (Salesforce Access Token) that will be used to retrieve the Composer template and Salesforce data for the merge.
Document Generation
When you save the following sample Apex code class into the Salesforce Org, you can directly invoke the public function initiateCongaAPI() from the Salesforce Developer Console (i.e. CongaAPI.initiateCongaAPI();) to initiate a Conga API merge.
In this sample code, we merge the document into PDF format and then email it to a Salesforce Contact with one Salesforce attachment. All of these composer parameters are added to the LegacyOptions section of the Conga Ingress API call. For more information, see Step 3.
Sample Code (It may differ depending on your usage)
This function assembles all four required API calls. The following is the description of each of these four API calls.
-
Conga Authorization Access Token API call: In the sample code, the function that completed this task is called retrieveCongaAuthToken(). This API call returns the Conga-access-token, which will be used to authorize the Conga Ingress API call and the Conga Status-Service API call.
- HTTP Method: POST
- Server URL: https://login-rls.congacloud.com/api/v1/auth/connect/token
- Required Headers: Content-Type: application/x-www-form-urlencoded
-
Post Body:
grant_type
‘client_credentials’
Scope
‘doc-gen.composer’
client_id
Retrieved from the Composer Setup Menu, and set in the code variable congaAuthClientId.
client_secret
Retrieved from the Composer Setup Menu, and set in the code variable congaAuthClientSecret.
-
Sample Response:
{ "access_token": "eyJh………… ", "expires_in": 3600, "token_type": "Bearer", "scope": "doc-gen.composer" }
-
Salesforce Auth Access Token API call: In the sample code, the function that completed this task is called retrieveSalesforceAccessToken(). This API call returns the Salesforce-access-token, which will be used to authorize access to all the Salesforce data and Composer template stored in the Salesforce org.In the sample code above the current user session is used for the merge. This is how the legacy composer solutions behave. However, it may be preferable to use an integration user for the API to control permissions.
Sample Code (It may differ depending on your usage):
// If Salesforce OAuth is desired, a connected app needs to be created within the Salesforce Org. Copy the Client_Id value to salesforceConnectedAppClientId and the Client_Secret value to salesforceConnectedAppClientSecret. private static String salesforceConnectedAppClientId = 'SALESFORCE_CONNECTED_APP_CLIENT_ID'; private static String salesforceConnectedAppClientSecret = 'SALESFORCE_CONNECTED_APP_CLIENT_SECRET'; private static String orgUserName = 'SFDC_USER_PSWD'; private static String orgUserPassword = 'SFDC_USER_PSWD'; // call the Salesforce Auth endpoint to get Salesforce-AccessToken private static String retrieveSalesforceAccessToken() { try { HttpRequest req = new HttpRequest(); String postBody = 'grant_type=password&username=' + orgUserName + '&password=' + orgUserPassword+ '&client_id=' + salesforceConnectedAppClientId + '&client_secret=' + salesforceConnectedAppClientSecret; req.setBody(postBody); req.setHeader('Content-Length', String.valueOf(postBody.Length())); req.setHeader('Content-Type', 'application/x-www-form-urlencoded'); // change the auth endpoint to https://login.salesforce.com/services/oauth2/token for production or dev org. req.setEndpoint('https://test.salesforce.com/services/oauth2/token'); req.setMethod('POST'); req.setTimeout(30000); HttpResponse res = new Http().send(req); return res.getBody(); } catch(Exception e) { throw new CongaHandledException(Label.Toast_General_Merge_Error).withMessage(e.getMessage()); } } This modified method makes an API call using additional parameters to get a session associated with the Connected App identified by salesforceConnectedAppClientId and salesforceConnectedAppClientSecret.
Note:You can also retrieve the Salesforce Auth Access Token using any other way. More information can be found in the Salesforce documentation
- HTTP Method: POST
-
Server URL: - Sandbox:
https://test.salesforce.com/services/oauth2/token
- Production: https://login.salesforce.com/services/oauth2/token
-
Required Headers:- Content-Type: application/x-www-form-urlencoded
- Content-Length: String.valueOf(postBody.Length())
-
Post Body:
grant_type
‘password’
scope
‘doc-gen.composer’
client_id & client_secret
Create your own Salesforce connected app, get the Consumer Key and Consumer Secret, and set in the code variables salesforceConnectedAppClientId and salesforceConnectedAppClientSecret respectively.
username
‘{org user name}’
password
‘{org user password}’
-
Sample Response:
{ "access_token": "………………", "instance_url": "https://speed-inspiration-5029-dev-ed.cs40.my.salesforce.com", "id": "https://test.salesforce.com/id/00D9A000000NlEbUAK/0058A000009SUwTQAW", "token_type": "Bearer", "issued_at": "1649798597751", "signature": "1p0VXuOaqgppeP8oMBOnQqhgwt6Fr6plKi8SaYkfMYo=" }
For more information on Salesforce Auth Access Token, see Salesforce OAuth and JWT Documentation.
-
Composer merge process Ingress API call: In the sample code, the function that completed this task is called invokeCongaIngressApi(). This API call initiates the merge process and returns a correlationId that will be used in the next status-service call (see step 4).
- HTTP Method: POST
- Server URL: https://coreapps-rlsprod.congacloud.com/api/ingress/v1/Merge
-
Required Headers:- Content-Type: application/json- Content-Length: String.valueOf(postBody.Length())
- Authorization: Bearer + Conga Auth Access Token from the first Api call
-
Post Body:
{ "SalesforceRequest": { "sessionId": <Salesforce AccessToken from the second API call>, "TemplateId":<set in the code variable composerTemplateId>, "MasterId": <set in the code variable salesforceMasterObjId>, "ServerUrl": <Salesforce serverUrl from the second API call> + ‘/services/Soap/u/50.0/’ + <set in the code variable orgId> } "LegacyOptions": {"sc0":"1","sc1":"attachments","DeFaultpdf":"1","Pdfa":"1a","DS7":"12","EmailToId":"0048e00001WzHFQAA3", "CongaEmailTemplateId":"a036e00000BjBriAAF","attachmentid":"0991e0000018STQ"} } For more information on supported parameters in the legacyOptions section for the Composer API request, click here.
-
Sample Response:
{ "correlationId": "<correlationId>", "status": "Accepted", "result": { "statusCode": "Success", "statusMessage": [ { "code": "SUCC200", "description": "Success" } ] } }
-
Composer merge-status API call: In the sample code, the function that completed this task is called invokeCongaStatusServiceApi(). This API call checks the current merge's status and returns the status of all completed and ongoing merge steps. Call this API repeatedly until the response returns a message with the string "message":"Completed."
- HTTP Method: GET
- Server URL: https://coreapps-rlsprod.congacloud.com/api/ingress/v1/status/ + { correlationId from the step 3 API call response}
-
Required Headers:
- Authorization: Bearer + Conga Auth Access Token from the first API call
-
Sample Response:
[ "status": { "version": "1.0.0", "message": "Pending", "detail": "Event publish request from Ingress", "startDateTime": "2022-04-11T16:00:56.0204229Z", "endDateTime": null, "statusCode": 201 } ]
Using Status Service:
The Status service should be an integral part of your API solution. For each API request, the Status service should be polled. When it is used correctly, your solution can monitor the progress of the merge and provide updates along the way in processing your requests.
The status service provides not only flow updates such as InProgress to Completed, but it also reports errors in your request composition. Missing data, missing template ids, missing session ids, missing or misconfigured JSON data, and merge fields, such as TableStart and TableEnd, are examples of what the status service can provide detail on for requests as they move through the API flow.
Using and integrating the Status service into your solutions can provide earlier notifications and reduce the need to contact support for troubleshooting issues when they arise. You could use the status service to notify administrators or end users about the status.
Security Considerations
Endpoint Authentication in Salesforce Apex:
It is recommended to use Salesforce platform mechanisms to protect credentials used for Conga platform callouts (and Salesforce platform if applicable). These callouts require credentials that should be securely stored on the Salesforce platform rather than hard coded. It is also recommended to configure Named Credentials for the apex callouts.
Salesforce IP Restrictions:
Obtaining a new session from Salesforce to be used for merging will always require a call from a trusted IP address or Connected App. This is required if executed within Salesforce as Apex, in a client browser, or on an external application server. Trusted IP will only be required for the origin of the login call; whitelisting the Conga platform is not required. There are three ways to build trust.
- Relax the IP restrictions for the OAuth-connected app that is being used to obtain the session.
- Add the caller IP address (the client making the login call) to the list of Trusted IP ranges:
- For the Organization Network Access
- On the Salesforce User’s Profile
- Append the Salesforce User Security Token to the password.
For more information, see Salesforce Security and the API.