diff --git a/benchmark_test.go b/benchmark_test.go
index d7ed9d535ff31b5a28eb0a14fd3e7754680f1da7..312c139812d029f116aefa8f03936d4b49d6d4ce 100644
--- a/benchmark_test.go
+++ b/benchmark_test.go
@@ -27,7 +27,7 @@ func BenchmarkClientHTTPEcho(b *testing.B) {
 		eg.SetLimit(4)
 		for i := 0; i < 1000; i++ {
 			eg.Go(func() error {
-				return client.Call(nil, "test_echoAny", []any{1, 2, 3, 4, 56, 6, wantBack, wantBack, wantBack})
+				return client.Call(nil, nil, "test_echoAny", []any{1, 2, 3, 4, 56, 6, wantBack, wantBack, wantBack})
 			})
 		}
 		eg.Wait()
@@ -48,7 +48,7 @@ func BenchmarkClientHTTPEchoEmpty(b *testing.B) {
 		eg.SetLimit(4)
 		for i := 0; i < 1000; i++ {
 			eg.Go(func() error {
-				return client.Call(nil, "test_echoAny", 0)
+				return client.Call(nil, nil, "test_echoAny", 0)
 			})
 		}
 		eg.Wait()
@@ -76,7 +76,7 @@ func BenchmarkClientWebsocketEcho(b *testing.B) {
 		eg.SetLimit(4)
 		for i := 0; i < 1000; i++ {
 			eg.Go(func() error {
-				return client.Call(nil, "test_echoAny", payload)
+				return client.Call(nil, nil, "test_echoAny", payload)
 			})
 		}
 		eg.Wait()
@@ -98,7 +98,7 @@ func BenchmarkClientWebsocketEchoEmpty(b *testing.B) {
 		eg.SetLimit(4)
 		for i := 0; i < 1000; i++ {
 			eg.Go(func() error {
-				return client.Call(nil, "test_echoAny", 0)
+				return client.Call(nil, nil, "test_echoAny", 0)
 			})
 		}
 		eg.Wait()
diff --git a/client.go b/client.go
index 8fa8446edd36af258f6c1241317a3e7d485603c6..184d0f18ab42a5db39a5f9d8d5203a58807c1e99 100644
--- a/client.go
+++ b/client.go
@@ -233,7 +233,7 @@ func (c *Client) SupportedModules() (map[string]string, error) {
 	var result map[string]string
 	ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout)
 	defer cancel()
-	err := c.CallContext(ctx, &result, "rpc_modules")
+	err := c.Call(ctx, &result, "rpc_modules")
 	return result, err
 }
 
@@ -297,19 +297,17 @@ func (c *Client) call(ctx context.Context, result any, msg *jsonrpcMessage) erro
 //
 // The result must be a pointer so that package json can unmarshal into it. You
 // can also pass nil, in which case the result is ignored.
-func (c *Client) Do(ctx context.Context, result any, method string, param any) error {
-	return c.DoContext(ctx, result, method, param)
-}
-
-// DEPRECATED: use Do
-func (c *Client) DoContext(ctx context.Context, result any, method string, param any) error {
+func (c *Client) Do(ctx context.Context, result any, method string, params any) error {
 	if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr {
 		return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result)
 	}
-	msg, err := c.newMessageP(method, param)
+	msg, err := c.newMessageP(method, params)
 	if err != nil {
 		return err
 	}
+	if ctx == nil {
+		ctx = context.TODO()
+	}
 	return c.call(ctx, result, msg)
 }
 
@@ -318,11 +316,6 @@ func (c *Client) Call(ctx context.Context, result any, method string, args ...an
 	return c.Do(ctx, result, method, args)
 }
 
-// DEPRECATED: use Call
-func (c *Client) CallContext(ctx context.Context, result any, method string, args ...any) error {
-	return c.DoContext(ctx, result, method, args)
-}
-
 // BatchCall sends all given requests as a single batch and waits for the server
 // to return a response for all of them.
 //
@@ -331,15 +324,14 @@ func (c *Client) CallContext(ctx context.Context, result any, method string, arg
 //
 // Note that batch calls may not be executed atomically on the server side.
 func (c *Client) BatchCall(ctx context.Context, b ...BatchElem) error {
-	return c.BatchCallContext(ctx, b)
-}
-
-// DEPRECATED: use BatchCall
-func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
 	var (
 		msgs = make([]*jsonrpcMessage, len(b))
 		byID = make(map[string]int, len(b))
 	)
+
+	if ctx == nil {
+		ctx = context.TODO()
+	}
 	op := &requestOp{
 		ids:  make([]json.RawMessage, len(b)),
 		resp: make(chan *jsonrpcMessage, len(b)),
diff --git a/client_test.go b/client_test.go
index bd856b1ca7a5613ac11437f30154b850daf3b2c6..44cca239575a9904c688811ec95fc1f9adc642e5 100644
--- a/client_test.go
+++ b/client_test.go
@@ -30,9 +30,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/davecgh/go-spew/spew"
 	"tuxpa.in/a/zlog"
 	"tuxpa.in/a/zlog/log"
-	"github.com/davecgh/go-spew/spew"
 )
 
 func init() {
@@ -46,7 +46,7 @@ func TestClientRequest(t *testing.T) {
 	defer client.Close()
 
 	var resp echoResult
-	if err := client.Call(&resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
+	if err := client.Call(nil, &resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
 		t.Fatal(err)
 	}
 	if !reflect.DeepEqual(resp, echoResult{"hello", 10, &echoArgs{"world"}}) {
@@ -60,12 +60,12 @@ func TestClientResponseType(t *testing.T) {
 	client := DialInProc(server)
 	defer client.Close()
 
-	if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
+	if err := client.Call(nil, nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
 		t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
 	}
 	var resultVar echoResult
 	// Note: passing the var, not a ref
-	err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"})
+	err := client.Call(nil, resultVar, "test_echo", "hello", 10, &echoArgs{"world"})
 	if err == nil {
 		t.Error("Passing a var as result should be an error")
 	}
@@ -79,7 +79,7 @@ func TestClientErrorData(t *testing.T) {
 	defer client.Close()
 
 	var resp any
-	err := client.Call(&resp, "test_returnError")
+	err := client.Call(nil, &resp, "test_returnError")
 	if err == nil {
 		t.Fatal("expected error")
 	}
@@ -120,7 +120,7 @@ func TestClientBatchRequest(t *testing.T) {
 			Result: new(int),
 		},
 	}
-	if err := client.BatchCall(batch); err != nil {
+	if err := client.BatchCall(nil, batch...); err != nil {
 		t.Fatal(err)
 	}
 	wantResult := []BatchElem{
@@ -162,7 +162,7 @@ func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) }
 func TestClientCancelHTTP(t *testing.T)      { testClientCancel("http", t) }
 func TestClientCancelIPC(t *testing.T)       { testClientCancel("ipc", t) }
 
-// This test checks that requests made through CallContext can be canceled by canceling
+// This test checks that requests made through Call can be canceled by canceling
 // the context.
 func testClientCancel(transport string, t *testing.T) {
 	// These tests take a lot of time, run them all at once.
@@ -234,7 +234,7 @@ func testClientCancel(transport string, t *testing.T) {
 
 			// Now perform a call with the context.
 			// The key thing here is that no call will ever complete successfully.
-			err := client.CallContext(ctx, nil, "test_block")
+			err := client.Call(ctx, nil, "test_block")
 			switch {
 			case err == nil:
 				_, hasDeadline := ctx.Deadline()
@@ -307,7 +307,7 @@ func TestClientHTTP(t *testing.T) {
 	for i := range results {
 		i := i
 		go func() {
-			errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args)
+			errc <- client.Call(nil, &results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args)
 		}()
 	}
 
@@ -357,7 +357,7 @@ func TestClientReconnect(t *testing.T) {
 
 	// Perform a call. This should work because the server is up.
 	var resp echoResult
-	if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil {
+	if err := client.Call(ctx, &resp, "test_echo", "", 1, nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -368,7 +368,7 @@ func TestClientReconnect(t *testing.T) {
 	time.Sleep(2 * time.Second)
 
 	// Try calling again. It shouldn't work.
-	if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil {
+	if err := client.Call(ctx, &resp, "test_echo", "", 2, nil); err == nil {
 		t.Error("successful call while the server is down")
 		t.Logf("resp: %#v", resp)
 	}
@@ -385,7 +385,7 @@ func TestClientReconnect(t *testing.T) {
 		go func() {
 			<-start
 			var resp echoResult
-			errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil)
+			errors <- client.Call(ctx, &resp, "test_echo", "", 3, nil)
 		}()
 	}
 	close(start)
diff --git a/conn.go b/conn.go
index 47d5334298d72c8e74ac008d1d4dfc842fdba4d4..9bfc7d11c76eb9175e4766dff70d318f723fe5c0 100644
--- a/conn.go
+++ b/conn.go
@@ -5,10 +5,13 @@ import "context"
 type Conn interface {
 	Do(ctx context.Context, result any, method string, params any) error
 	Call(ctx context.Context, result any, method string, params ...any) error
-	BatchCall(b ...BatchElem) error
+	BatchCall(ctx context.Context, b ...BatchElem) error
+	SetHeader(key, value string)
+	Close() error
 }
 
 type SubscriptionConn interface {
+	Conn
 	Notify(ctx context.Context, method string, args ...any) error
 	Subscribe(ctx context.Context, namespace string, channel any, args ...any) (*ClientSubscription, error)
 }
diff --git a/http_test.go b/http_test.go
index 6fb56af150cc9a4117616976015798b5fb7f6054..a23d99ad14c02bf339324ca657ed5e9df660c7bf 100644
--- a/http_test.go
+++ b/http_test.go
@@ -118,7 +118,7 @@ func TestHTTPRespBodyUnlimited(t *testing.T) {
 	defer c.Close()
 
 	var r string
-	if err := c.Call(&r, "test_largeResp"); err != nil {
+	if err := c.Call(nil, &r, "test_largeResp"); err != nil {
 		t.Fatal(err)
 	}
 	if len(r) != respLength {
@@ -140,7 +140,7 @@ func TestHTTPErrorResponse(t *testing.T) {
 	}
 
 	var r string
-	err = c.Call(&r, "test_method")
+	err = c.Call(nil, &r, "test_method")
 	if err == nil {
 		t.Fatal("error was expected")
 	}
@@ -180,7 +180,7 @@ func TestHTTPPeerInfo(t *testing.T) {
 
 	// Request peer information.
 	var info PeerInfo
-	if err := c.Call(&info, "test_peerInfo"); err != nil {
+	if err := c.Call(nil, &info, "test_peerInfo"); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/subscription.go b/subscription.go
index 52d0900f82b554c489c0795a4e2a425d99096a34..527e6b0b46b33a2606423a02e2a584d8f3cba3a1 100644
--- a/subscription.go
+++ b/subscription.go
@@ -310,5 +310,5 @@ func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, e
 
 func (sub *ClientSubscription) requestUnsubscribe() error {
 	var result interface{}
-	return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid)
+	return sub.client.Call(context.Background(), &result, sub.namespace+unsubscribeMethodSuffix, sub.subid)
 }
diff --git a/testservice_test.go b/testservice_test.go
index 43e47062d8e04e3f5e8fa726ab176dd9d6a46bc6..bc1f919a415a7960ea91f8319e1ec36573a28f5b 100644
--- a/testservice_test.go
+++ b/testservice_test.go
@@ -123,7 +123,7 @@ func (s *testService) CallMeBack(ctx context.Context, method string, args []any)
 		return nil, errors.New("no client")
 	}
 	var result any
-	err := c.Call(&result, method, args...)
+	err := c.Call(nil, &result, method, args...)
 	return result, err
 }
 
@@ -135,7 +135,7 @@ func (s *testService) CallMeBackLater(ctx context.Context, method string, args [
 	go func() {
 		<-ctx.Done()
 		var result any
-		c.Call(&result, method, args...)
+		c.Call(nil, &result, method, args...)
 	}()
 	return nil
 }
diff --git a/websocket_test.go b/websocket_test.go
index 5893226d4e2e99b4dc1a4122483bd97309f96f18..8d0c2009370cbea783df0578e97be423192d342b 100644
--- a/websocket_test.go
+++ b/websocket_test.go
@@ -93,7 +93,7 @@ func TestWebsocketLargeCall(t *testing.T) {
 	// This call sends slightly less than the limit and should work.
 	var result echoResult
 	arg := strings.Repeat("x", maxRequestContentLength-200)
-	if err := client.Call(&result, "test_echo", arg, 1); err != nil {
+	if err := client.Call(nil, &result, "test_echo", arg, 1); err != nil {
 		t.Fatalf("valid call didn't work: %v", err)
 	}
 	if result.String != arg {
@@ -102,7 +102,7 @@ func TestWebsocketLargeCall(t *testing.T) {
 
 	// This call sends twice the allowed size and shouldn't work.
 	arg = strings.Repeat("x", maxRequestContentLength*2)
-	err = client.Call(&result, "test_echo", arg)
+	err = client.Call(nil, &result, "test_echo", arg)
 	if err == nil {
 		t.Fatal("no error for too large call")
 	}
@@ -125,7 +125,7 @@ func TestWebsocketPeerInfo(t *testing.T) {
 
 	// Request peer information.
 	var connInfo PeerInfo
-	if err := c.Call(&connInfo, "test_peerInfo"); err != nil {
+	if err := c.Call(nil, &connInfo, "test_peerInfo"); err != nil {
 		t.Fatal(err)
 	}
 
@@ -162,7 +162,7 @@ func TestClientWebsocketLargeMessage(t *testing.T) {
 	}
 
 	var r string
-	if err := c.Call(&r, "test_largeResp"); err != nil {
+	if err := c.Call(nil, &r, "test_largeResp"); err != nil {
 		t.Fatal("call failed:", err)
 	}
 	if len(r) != respLength {