Close service jobs

Learn how to set fulfillment data and set a job as complete.

Learn how to set fulfillment data, such as:

  • Start and end date-times
  • Fulfillment resources
  • Fulfillment documents such as Proof Of Appointment (POA) files
  • Job status, such as completed or cancelled

To set a job as complete, use the following operations:

Before you set a job as complete, you must provide appointment fulfillment data, using setAppointmentFulfillmentData and createServiceDocumentUploadDestination. There are four types of fulfillment data:

  • Estimated Arrival Time: The range of time when the technician is expected to arrive at the fulfillment location.
  • Appointment Fulfillment Times: The actual fulfillment date-time start and end.
  • Appointment Fulfillment Resources: The actual resources that fulfilled an appointment.
  • Appointment Fulfillment Documents: Documents that:
    • Serve as evidence of service completion
    • Were used in the course of fulfillment
    • Record aspects of fulfillment

📘

Note

The Amazon business line and service sellers determine the minimum requirements for fulfillment data and documents to close a job. Service sellers should confer with their relevant Amazon business line partners to settle appropriate API usage before they integrate with the Services API.

Prerequisites

To complete this tutorial, you need:

  • Authorization from the seller for whom you are making calls. Refer to Authorizing Selling Partner Applications for more information.
  • A service job identifier assigned to the service provider. Only jobs assigned to the service provider can be accessed. Job status are verified as in NotServiced state.
  • A scheduled appointment identifier pre-configured in Amazon Systems for the service provider.
  • An active appointment.

Step 1. Set or update service appointment fulfillment data

The setAppointmentFulfillmentData operation updates multiple aspects of service job appointment fulfillment data that you must record before you set a job as complete.

Step 2. Upload a fulfillment document

The createServiceDocumentUploadDestination operation creates a storage destination and identifier for a fulfillment document. This document is necessary to set a job as complete.

To upload a document:

  1. Create an upload destination for the service document with the createServiceDocumentUploadDestination operation.
  2. Encrypt and upload the file.

Use the information the response provides to encrypt and upload the file.

The following Java sample encrypts and uploads a feed. This sample code uses the Apache HTTP client.

  1. Use the following as input for the sample code:
    a. Your file content as the argument for the stream parameter of the InputStream method of the UploadToDestinationExample class.
    b. The initializationVector and key values from the createServiceDocumentUploadDestination response as arguments for the iv and key parameters of the EncryptionDetails method of the EncryptionDetails class.
    c. The url value from the createServiceDocumentUploadDestination response as the argument for the url parameter of the uploadToDestination method of the UploadToDestinationExample class.
  2. Save the sha256sum value to pass as the ContentSha256 parameter in setAppointmentFulfillmentData.
package com.amazon.spapi;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.HttpClients;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class UploadToDestinationExample {
    static final String SHA_256 = "SHA-256";
    static final String AES = "AES";
    static final int AES_BLOCK_SIZE = 16;
    static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
    static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
    static final CryptoProvider AES_CRYPTO_PROVIDER = UploadToDestinationExample::getInitializedCipher;

    static InputStream buildCipherInputStream(EncryptionDetails encryptionDetails, InputStream stream, int mode) {
        return new CipherInputStream(stream,
            AES_CRYPTO_PROVIDER.getInitializedCipher(mode, encryptionDetails));
    }

    private static InputStream buildCipherInputStream(EncryptionDetails encryptionDetails, InputStream stream) {
        return buildCipherInputStream(encryptionDetails, stream, Cipher.ENCRYPT_MODE);
    }

    static Cipher getInitializedCipher(int mode, EncryptionDetails details) {
        Cipher cipher;
        try {
            cipher = Cipher.getInstance(AES);
            Key key = new SecretKeySpec(BASE64_DECODER.decode(details.getKey()), AES);
            byte[] iv = BASE64_DECODER.decode(details.getIv());
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(mode, key, ivParameterSpec, new SecureRandom());
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException("Could not create Cipher for key-iv pair", e);
        }
        return cipher;
    }

    UploadDetails uploadToDestination(EncryptionDetails encryptionDetails, String uploadDestinationId, String url,
        String contentType,
        long documentLength,
        InputStream inputStream) {
        try {
            inputStream = buildCipherInputStream(encryptionDetails, inputStream);
            DigestInputStream sha256sumStream = new DigestInputStream(inputStream, MessageDigest.getInstance(SHA_256));
            inputStream = sha256sumStream;
            // Put the file
            HttpClient httpClient = HttpClients.createDefault();
            HttpPut httpPut = new HttpPut(url);
            // This content length calculation is specific to AES
            long contentLength = (documentLength / AES_BLOCK_SIZE + 1) *
                AES_BLOCK_SIZE;
            // This sets the Content-Length header since we specified the contentLength
            HttpEntity document = new InputStreamEntity(inputStream,
                contentLength);
            httpPut.setHeader("Content-Type", contentType);
            httpPut.setEntity(document);
            //Put the content in the S3 Pre-signed URL
            HttpResponse httpResponse = httpClient.execute(httpPut);
            if (httpResponse == null ||
                (httpResponse.getStatusLine().getStatusCode() / 100) != 2) {
                // Handle error responses here.
                throw new IllegalStateException("Could not upload to S3.");
            }
            // Document was successfully uploaded!
            byte[] sha256sumDigest = sha256sumStream.getMessageDigest().digest();
            String sha256sum = BASE64_ENCODER.encodeToString(sha256sumDigest);
            return new UploadDetails(uploadDestinationId, sha256sum);
        } catch (IOException | NoSuchAlgorithmException e) {
            throw new RuntimeException("Error occurred when attempting to encrypt or upload to S3.", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }
}

Step 3. Complete a service job

The completeServiceJobByServiceJobId operation marks a service job as complete. If the given job is the only active job in the order, then it marks the entire order as complete.

Step 4. Cancel a service job

The cancelServiceJobByServiceJobId operation marks a service job as cancelled. If the given job is the only active job in the order, then it marks the entire order as cancelled.

Cancellation reason codes by marketplace

Reason code Reason description Default Heavy bulk
S1 Customer didn't show up. US, IN, UK, DE US, IN, UK, DE
S2 Customer outside service area. - DE
S3 Work is different than scoped and order cannot be completed. US, IN, UK, DE US, IN, UK, DE
S4 Provider unable to contact customer. - DE
S5 Provider cannot meet customer's schedule. - DE
S6 Job requires professional trade license. - DE
S7 - S12 Codes not in use. - -
S13 Provider cannot meet original appointment. US, IN, UK, DE US, IN, UK
S14 Provider cannot meet new appointment request. US US
S15 Problem with Amazon product or its delivery. US, IN, UK, DE US, IN, UK
S16 Problem with non-Amazon product, delivery, or service. US, UK, DE US, UK
S17 I don't need this service. US, IN US, IN
S18 - S19 Codes not in use. - -
S20 Too expensive. IN IN
S21 - S26 Codes not in use. - -
S27 Brand installed. IN IN
S28 Coverage area changed. - -
S29 Pricing mistake. - -
S30 No longer works with customer schedule. - -
S31 Product damaged. IN -
S32 Risky site. IN -
S33 Customer wants cancellation. IN -
S34 Site not ready. IN -
S35 Product not delivered. IN -
S36 Additional material needed. IN -
S37 Customer wants reschedule. IN -
S38 Product returned. IN -
S39 We could not reach you. IN -
S40 Service already completed. IN -
S41 Service in another location. IN -
S42 Needs revisit. IN -
S43 Documents not available. IN -
S44 Slot missed. IN -
S45 Parts not available. IN -
S46 Wrong completion. IN -
S47 Need customer approval for repair. IN -
S48 - S50 Codes not in use. - -
S51 Installation done by third party and customer does not require installation. IN -
S52 Product damaged, non-installable product, customer returned the product, or product gifted. IN -
S53 - S54 Codes not in use. - -
S55 Restriction on visit. IN -

Select a cancellation reason code

  1. Call the getServiceJobByServiceJobId operation and check for the marketplaceId value to identify the marketplace details associated with the service order of a job.
  2. Call the getServiceJobByServiceJobId operation and check for the serviceType value to identify the service type of the job.
    • If serviceType is heavybulky, then search for a reason code in the Heavy Bulky column.
    • Otherwise, search for a reason code in the Default column.
  3. Select a reason code suitable for your use case and pass it as a query parameter when you request a job for cancellation.