If you perform an API-call, all synchronous activites (that don't have activiti:async="true") are executed in the calling thread, until a wait-state is reached, an async-activity is found or the end of the process is reached. If there is an exception in the steps performed until that point, you'll get an ActivitiException back from the API-call, informing you about the failure.
If you have asynchronous execution of a process (being either a timer or an asynchronous activity), all exceptions that occur during execution, are stored in the associated job. You can find out what jobs have failed, using the JobQuery (managementService), narrowing them down using the process-instance id. If the query results in one or more jobs that have an error for that particular process-instance, it has failed. You can inspect the exception stack trace by using the ManagementService.getJobExceptionStacktrace() method…