diff --git a/rpc/api/api.go b/rpc/api/api.go
index e870ec58ea5f9445a982de41898494f7b15327e1..20664794643ec340463bf0860a4badc8b5d5760b 100644
--- a/rpc/api/api.go
+++ b/rpc/api/api.go
@@ -13,7 +13,8 @@ const (
 	MergedApiName   = "merged"
 	MinerApiName    = "miner"
 	NetApiName      = "net"
-	txPoolApiName   = "txpool"
+	ShhApiName      = "shh"
+	TxPoolApiName   = "txpool"
 	PersonalApiName = "personal"
 	Web3ApiName     = "web3"
 )
@@ -21,7 +22,8 @@ const (
 var (
 	// List with all API's which are offered over the IPC interface by default
 	DefaultIpcApis = strings.Join([]string{
-		AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, txPoolApiName, PersonalApiName, Web3ApiName,
+		AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
+		ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
 	}, ",")
 )
 
diff --git a/rpc/api/shh.go b/rpc/api/shh.go
new file mode 100644
index 0000000000000000000000000000000000000000..04c53c93eda352892bc29db37187ba7dffbb551c
--- /dev/null
+++ b/rpc/api/shh.go
@@ -0,0 +1,171 @@
+package api
+
+import (
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/eth"
+	"github.com/ethereum/go-ethereum/rpc/codec"
+	"github.com/ethereum/go-ethereum/rpc/shared"
+	"github.com/ethereum/go-ethereum/xeth"
+)
+
+var (
+	// mapping between methods and handlers
+	shhMapping = map[string]shhhandler{
+		"shh_version":          (*shhApi).Version,
+		"shh_post":             (*shhApi).Post,
+		"shh_hasIdentity":      (*shhApi).HasIdentity,
+		"shh_newIdentity":      (*shhApi).NewIdentity,
+		"shh_newFilter":        (*shhApi).NewFilter,
+		"shh_uninstallFilter":  (*shhApi).UninstallFilter,
+		"shh_getFilterChanges": (*shhApi).GetFilterChanges,
+	}
+)
+
+func newWhisperOfflineError(method string) error {
+	return shared.NewNotAvailableError(method, "whisper offline")
+}
+
+// net callback handler
+type shhhandler func(*shhApi, *shared.Request) (interface{}, error)
+
+// shh api provider
+type shhApi struct {
+	xeth     *xeth.XEth
+	ethereum *eth.Ethereum
+	methods  map[string]shhhandler
+	codec    codec.ApiCoder
+}
+
+// create a new whisper api instance
+func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi {
+	return &shhApi{
+		xeth:     xeth,
+		ethereum: eth,
+		methods:  shhMapping,
+		codec:    coder.New(nil),
+	}
+}
+
+// collection with supported methods
+func (self *shhApi) Methods() []string {
+	methods := make([]string, len(self.methods))
+	i := 0
+	for k := range self.methods {
+		methods[i] = k
+		i++
+	}
+	return methods
+}
+
+// Execute given request
+func (self *shhApi) Execute(req *shared.Request) (interface{}, error) {
+	if callback, ok := self.methods[req.Method]; ok {
+		return callback(self, req)
+	}
+
+	return nil, shared.NewNotImplementedError(req.Method)
+}
+
+func (self *shhApi) Name() string {
+	return ShhApiName
+}
+
+func (self *shhApi) Version(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	return w.Version(), nil
+}
+
+func (self *shhApi) Post(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	args := new(WhisperMessageArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+
+	err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
+	if err != nil {
+		return false, err
+	}
+
+	return true, nil
+}
+
+func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	args := new(WhisperIdentityArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+
+	return w.HasIdentity(args.Identity), nil
+}
+
+func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	return w.NewIdentity(), nil
+}
+
+func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) {
+	args := new(WhisperFilterArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+
+	id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics)
+	return newHexNum(big.NewInt(int64(id)).Bytes()), nil
+}
+
+func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) {
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+	return self.xeth.UninstallWhisperFilter(args.Id), nil
+}
+
+func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	// Retrieve all the new messages arrived since the last request
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+
+	return self.xeth.WhisperMessagesChanged(args.Id), nil
+}
+
+func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) {
+	w := self.xeth.Whisper()
+	if w == nil {
+		return nil, newWhisperOfflineError(req.Method)
+	}
+
+	// Retrieve all the cached messages matching a specific, existing filter
+	args := new(FilterIdArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, err
+	}
+
+	return self.xeth.WhisperMessages(args.Id), nil
+}
diff --git a/rpc/api/shh_args.go b/rpc/api/shh_args.go
new file mode 100644
index 0000000000000000000000000000000000000000..00abac232c1b05eaf0d66fded073b30d7eebbcff
--- /dev/null
+++ b/rpc/api/shh_args.go
@@ -0,0 +1,158 @@
+package api
+
+import (
+	"encoding/json"
+	"fmt"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type WhisperMessageArgs struct {
+	Payload  string
+	To       string
+	From     string
+	Topics   []string
+	Priority uint32
+	Ttl      uint32
+}
+
+func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []struct {
+		Payload  string
+		To       string
+		From     string
+		Topics   []string
+		Priority interface{}
+		Ttl      interface{}
+	}
+
+	if err = json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+	args.Payload = obj[0].Payload
+	args.To = obj[0].To
+	args.From = obj[0].From
+	args.Topics = obj[0].Topics
+
+	var num *big.Int
+	if num, err = numString(obj[0].Priority); err != nil {
+		return err
+	}
+	args.Priority = uint32(num.Int64())
+
+	if num, err = numString(obj[0].Ttl); err != nil {
+		return err
+	}
+	args.Ttl = uint32(num.Int64())
+
+	return nil
+}
+
+type WhisperIdentityArgs struct {
+	Identity string
+}
+
+func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	argstr, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("arg0", "not a string")
+	}
+
+	args.Identity = argstr
+
+	return nil
+}
+
+type WhisperFilterArgs struct {
+	To     string
+	From   string
+	Topics [][]string
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
+// JSON message blob into a WhisperFilterArgs structure.
+func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
+	// Unmarshal the JSON message and sanity check
+	var obj []struct {
+		To     interface{} `json:"to"`
+		From   interface{} `json:"from"`
+		Topics interface{} `json:"topics"`
+	}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+	// Retrieve the simple data contents of the filter arguments
+	if obj[0].To == nil {
+		args.To = ""
+	} else {
+		argstr, ok := obj[0].To.(string)
+		if !ok {
+			return shared.NewInvalidTypeError("to", "is not a string")
+		}
+		args.To = argstr
+	}
+	if obj[0].From == nil {
+		args.From = ""
+	} else {
+		argstr, ok := obj[0].From.(string)
+		if !ok {
+			return shared.NewInvalidTypeError("from", "is not a string")
+		}
+		args.From = argstr
+	}
+	// Construct the nested topic array
+	if obj[0].Topics != nil {
+		// Make sure we have an actual topic array
+		list, ok := obj[0].Topics.([]interface{})
+		if !ok {
+			return shared.NewInvalidTypeError("topics", "is not an array")
+		}
+		// Iterate over each topic and handle nil, string or array
+		topics := make([][]string, len(list))
+		for idx, field := range list {
+			switch value := field.(type) {
+			case nil:
+				topics[idx] = []string{}
+
+			case string:
+				topics[idx] = []string{value}
+
+			case []interface{}:
+				topics[idx] = make([]string, len(value))
+				for i, nested := range value {
+					switch value := nested.(type) {
+					case nil:
+						topics[idx][i] = ""
+
+					case string:
+						topics[idx][i] = value
+
+					default:
+						return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string")
+					}
+				}
+			default:
+				return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array")
+			}
+		}
+		args.Topics = topics
+	}
+	return nil
+}
diff --git a/rpc/api/ssh_js.go b/rpc/api/ssh_js.go
new file mode 100644
index 0000000000000000000000000000000000000000..f401f70f197cd811f120f087e1fa884c4919b910
--- /dev/null
+++ b/rpc/api/ssh_js.go
@@ -0,0 +1,30 @@
+package api
+
+const Shh_JS = `
+web3._extend({
+	property: 'shh',
+	methods:
+	[
+		new web3._extend.Method({
+			name: 'post',
+			call: 'shh_post',
+			params: 6,
+			inputFormatter: [web3._extend.formatters.formatInputString,
+							  web3._extend.formatters.formatInputString,
+							  web3._extend.formatters.formatInputString,
+							,
+							, web3._extend.formatters.formatInputInt
+							, web3._extend.formatters.formatInputInt],
+			outputFormatter: web3._extend.formatters.formatOutputBool
+		}),
+	],
+	properties:
+	[
+		new web3._extend.Property({
+			name: 'version',
+			getter: 'shh_version',
+			outputFormatter: web3._extend.formatters.formatOutputInt
+		})
+	]
+});
+`
diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go
index f340c501f391bd28eaff757ebedfc38cdacbcd4d..ebbe199b1bb4a29243d1b011baf4759a1076a911 100644
--- a/rpc/api/txpool.go
+++ b/rpc/api/txpool.go
@@ -56,7 +56,7 @@ func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) {
 }
 
 func (self *txPoolApi) Name() string {
-	return txPoolApiName
+	return TxPoolApiName
 }
 
 func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index b44a325a8b8c7ce75d1ee860b5d6b945764c30bf..ad8a97e9205f0e785cd1de1f5c2d4ddd611d5712 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -31,7 +31,9 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
 			apis[i] = NewMinerApi(eth, codec)
 		case NetApiName:
 			apis[i] = NewNetApi(xeth, eth, codec)
-		case txPoolApiName:
+		case ShhApiName:
+			apis[i] = NewShhApi(xeth, eth, codec)
+		case TxPoolApiName:
 			apis[i] = NewTxPoolApi(xeth, eth, codec)
 		case PersonalApiName:
 			apis[i] = NewPersonalApi(xeth, eth, codec)
@@ -55,7 +57,9 @@ func Javascript(name string) string {
 		return Miner_JS
 	case NetApiName:
 		return Net_JS
-	case txPoolApiName:
+	case ShhApiName:
+		return Shh_JS
+	case TxPoolApiName:
 		return TxPool_JS
 	case PersonalApiName:
 		return Personal_JS