How can a record be undeleted using the REST API?

It can't. Here are the HTTP verbs:

  • HEAD (retrieve resource metadata)
  • GET (retrieve information, such as basic resource summary information)
  • POST (create a new object)
  • PATCH (update a record)
  • DELETE (delete a record)

The undelete verb is conspicuously missing ;-) and there are zero occurrences of 'undelete' in 192 pages of documentation.

Force.com REST API Developer Guide

https://resources.docs.salesforce.com/sfdc/pdf/api_rest.pdf

On a whim, tried patching the IsDeleted flag on a deleted record. But to no avail:

> PATCH /services/data/v36.0/sobjects/Account/00158000004AWnF HTTP/1.1
> Content-Type: application/json
> {"IsDeleted":false}

< HTTP/1.1 404 Not Found
< Content-Type: application/json;charset=UTF-8
< [{"message":"entity is deleted","errorCode":"ENTITY_IS_DELETED","fields":[]}]

Same goes for posting with the Id of the deleted record specified:

> POST /services/data/v36.0/sobjects/Account/ HTTP/1.1
> Content-Type: application/json
> {"Id":"00158000004AWnF","IsDeleted":false}

< HTTP/1.1 400 Bad Request
< Content-Type: application/json;charset=UTF-8
< [{"message":"The Id field should not be specified in the sobject data.","errorCode":"INVALID_FIELD"}]

There is no undelete REST API natively built in .

All is not lost and one can always write a bit of apex to accomplish this functionality

//Execute Anonymous

Account a = new Account(Name='Trump');
insert(a);
insert(new Contact(LastName='Carter',AccountId=a.Id));
delete a;

//REST API to Undelete

@RestResource(urlMapping='/AccountUndelete/*')

 global with sharing class UndeleteRestResource {
   @HttpGet
  global static list<Account> doGet() {
     RestRequest req = RestContext.request;
     RestResponse res = RestContext.response;
     String accountname = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
     Account[] restoredAccts = [SELECT Id, Name FROM Account WHERE Name = :accountname  ALL ROWS]; 
     try {
        undelete restoredAccts ;
     } catch (DmlException e) {
     // Process exception here
     }
     return restoredAccts ;
   }
}

The HTTP verbs provide the base building blocks from which any more complex behaviour can be constructed by representing the life cycle of a "thing" (it can be created, its state can be ascertained, its state can be changed and it can be destroyed). The HTTP verbs POST, GET, PUT, DELETE respectively provide the methods to manage that life cycle.

Once a thing has been DELETED its life cycle has ended. But you seem to be treating "deleted" things as still engaged in a life cycle (ie they can be returned to a previous state). What you are talking about is not really the deletion and recreated from non-existence of a record but merely the transition of a record from state to state. In this case an active to an inactive state, and back again (presumably through some kind of archiving mechanism). Transition between these states can be handled with a PUT to the resource in question (or POST for transition of groups of resource instances).

If you don't want to have to go through and update your queries to ignore accounts with an inactive state you could actually have the resource move the raw data to an archive data store of some kind and out of the regular datastore (and back in if the account transitions back to an active state at a later date).