SIP Replaces support (RFC 3891 - “Replaces” Header)

This module implements support for Replaces header in PJSIP. The Replaces specification is written in RFC 3891 - The Session Initiation Protocol (SIP) “Replaces” Header, and can be used to enable a variety of features, for example: “Attended Transfer” and “Call Pickup”.

Using PJSIP Replaces Support


Application needs to call pjsip_replaces_init_module() during application initialization stage to register “replaces” support in PJSIP.

UAC Behavior: Sending a Replaces Header

A User Agent that wishes to replace a single existing early or confirmed dialog with a new dialog of its own, MAY send the target User Agent an INVITE request containing a Replaces header field. The User Agent Client (UAC) places the Call-ID, to-tag, and from-tag information for the target dialog in a single Replaces header field and sends the new INVITE to the target.

To initiate outgoing INVITE request with Replaces header, application would create the INVITE request with pjsip_inv_invite(), then adds pjsip_replaces_hdr instance into the request, filling up the Call-ID, To-tag, and From-tag properties of the header with the identification of the dialog to be replaced. Application may also optionally set the early_only property of the header to indicate that it only wants to replace early dialog.

Note that when the outgoing INVITE request (with Replaces) is initiated from an incoming REFER request (as in Attended Call Transfer case), this process should be done rather more automatically by PJSIP. Upon receiving incoming incoming REFER request, normally these processes will be performed:

  • Application finds Refer-To header,

  • Application creates outgoing dialog/invite session, specifying the URI in the Refer-To header as the initial remote target,

  • The URI in the Refer-To header may contain header parameters such as Replaces and Require headers.

  • The dialog keeps the header fields in the header parameters of the URI, and the invite session would add these headers into the outgoing INVITE request. Because of this, the outgoing INVITE request will contain the Replaces and Require headers.

For more information, please see the implementation of pjsua_call_xfer_replaces() in PJSUA API - High Level Softphone API source code.

UAS Behavior: Receiving a Replaces Header

The Replaces header contains information used to match an existing SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE with a Replaces header, the User Agent (UA) attempts to match this information with a confirmed or early dialog.

In PJSIP, if application wants to process the Replaces header in the incoming INVITE request, it should call pjsip_replaces_verify_request() before creating the INVITE session. The pjsip_replaces_verify_request() function checks and verifies the request to see if Replaces request can be processed. To be more specific, it performs the following verification:

  • checks that Replaces header is present. If not, the function will return PJ_SUCCESS without doing anything.

  • checks that no duplicate Replaces headers are present, or otherwise it will return 400 “Bad Request” response.

  • checks for matching dialog and verifies that the invite session has the correct state, and may return 481 “Call/Transaction Does Not Exist”, 603 “Declined”, or 486 “Busy Here” according to the processing rules specified in RFC 3891.

  • if matching dialog with correct state is found, it will give PJ_SUCCESS status and return the matching dialog back to the application.

The following pseudocode illustrates how application can process the incoming INVITE if it wants to support Replaces extension:

// Incoming INVITE request handler
pj_bool_t on_rx_invite(pjsip_rx_data *rdata)
  pjsip_dialog *dlg, *replaced_dlg;
  pjsip_inv_session *inv;
  pjsip_tx_data *response;
  pj_status_t status;

  // Check whether Replaces header is present in the request and process accordingly.
  status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response);
  if (status != PJ_SUCCESS) {
      // Something wrong with Replaces request.
      pj_status_t status;
      if (response) {
          status = pjsip_endpt_send_response(endpt, rdata, response, NULL, NULL);
          if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata);
      } else {
          // Respond with 500 (Internal Server Error)
          status = pjsip_endpt_respond_stateless(endpt, rdata, 500, NULL, NULL, NULL);
          if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata);

  // Create UAS Invite session as usual.
  status = pjsip_dlg_create_uas_and_inc_lock(.., rdata, .., &dlg);
  status = pjsip_inv_create_uas(dlg, .., &inv);

  // Send initial 100 "Trying" to the INVITE request
  status = pjsip_inv_initial_answer(inv, rdata, 100, ..., &response);
  if (status == PJ_SUCCESS)
      pjsip_inv_send_msg(inv, response);

  // This is where processing is different between normal call
  // (without Replaces) and call with Replaces.
  if (replaced_dlg) {
      pjsip_inv_session *replaced_inv;

      // Always answer the new INVITE with 200, regardless whether
      // the replaced call is in early or confirmed state.
      status = pjsip_inv_answer(inv, 200, NULL, NULL, &response);
      if (status == PJ_SUCCESS)
          pjsip_inv_send_msg(inv, response);

      // Get the INVITE session associated with the replaced dialog.
      replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);

      // Disconnect the "replaced" INVITE session.
      status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata);
      if (status == PJ_SUCCESS && tdata)
          status = pjsip_inv_send_msg(replaced_inv, tdata);

      // It's up to application to associate the new INVITE session
      // with the old (now terminated) session. For example, application
      // may assign the same User Interface object for the new INVITE
      // session.

  } else {
      // Process normal INVITE without Replaces.

For a complete sample implementation, please see pjsua_call_on_incoming() function of PJSUA API - High Level Softphone API in pjsua_call.c file.




pj_status_t pjsip_replaces_init_module(pjsip_endpoint *endpt)

Initialize Replaces support in PJSIP. This would, among other things, register the header parser for Replaces header.


endpt – The endpoint instance.


PJ_SUCCESS on success.

pjsip_replaces_hdr *pjsip_replaces_hdr_create(pj_pool_t *pool)

Create Replaces header.


pool – Pool to allocate the header instance from.


An empty Replaces header instance.

pj_status_t pjsip_replaces_verify_request(pjsip_rx_data *rdata, pjsip_dialog **p_dlg, pj_bool_t lock_dlg, pjsip_tx_data **p_tdata)

Verify that incoming request with Replaces header can be processed. This function will perform all necessary checks according to RFC 3891 Section 3 “User Agent Server Behavior: Receiving a Replaces Header”.

  • rdata – The incoming request to be verified.

  • p_dlg – On return, it will be filled with the matching dialog.

  • lock_dlg – Specifies whether this function should acquire lock to the matching dialog. If yes (and should be yes!), then application will need to release the dialog’s lock with pjsip_dlg_dec_lock() when the function returns PJ_SUCCESS and the p_dlg parameter is filled with the dialog instance.

  • p_tdata – Upon error, it will be filled with the final response to be sent to the request sender.


The function returns the following:

  • If the request doesn’t contain Replaces header, the function returns PJ_SUCCESS and p_dlg parameter will be set to NULL.

  • If the request contains Replaces header and a valid, matching dialog is found, the function returns PJ_SUCCESS and p_dlg parameter will be set to the matching dialog instance.

  • Upon error condition (as described by RFC 3891), the function returns non-PJ_SUCCESS, and p_tdata parameter SHOULD be set with a final response message to be sent to the sender of the request.

struct pjsip_replaces_hdr
#include <sip_replaces.h>

Declaration of SIP Replaces header (RFC 3891).

Public Functions

PJSIP_DECL_HDR_MEMBER(struct pjsip_replaces_hdr)

Standard header field.

Public Members

pj_str_t call_id


pj_str_t to_tag


pj_str_t from_tag


pj_bool_t early_only


pjsip_param other_param

Other parameters