{"id":21406591,"url":"https://github.com/ringcentral/ringcentral-softphone-ts","last_synced_at":"2025-10-19T16:12:58.269Z","repository":{"id":210758553,"uuid":"726913780","full_name":"ringcentral/ringcentral-softphone-ts","owner":"ringcentral","description":"RingCentral Softphone SDK","archived":false,"fork":false,"pushed_at":"2025-06-18T20:43:40.000Z","size":1251,"stargazers_count":7,"open_issues_count":0,"forks_count":5,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-06-18T20:48:49.214Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ringcentral.github.io/ringcentral-softphone-ts/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"PacoVu/ringcentral-softphone-ts","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ringcentral.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-03T19:08:58.000Z","updated_at":"2025-06-18T20:43:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"5c1d8ea5-d3b5-4a61-9b4b-769809588265","html_url":"https://github.com/ringcentral/ringcentral-softphone-ts","commit_stats":null,"previous_names":["ringcentral/ringcentral-softphone-ts"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ringcentral/ringcentral-softphone-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringcentral%2Fringcentral-softphone-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringcentral%2Fringcentral-softphone-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringcentral%2Fringcentral-softphone-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringcentral%2Fringcentral-softphone-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ringcentral","download_url":"https://codeload.github.com/ringcentral/ringcentral-softphone-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringcentral%2Fringcentral-softphone-ts/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265227898,"owners_count":23731059,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-22T16:40:46.433Z","updated_at":"2025-10-19T16:12:58.239Z","avatar_url":"https://github.com/ringcentral.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RingCentral Softphone SDK for TypeScript\n\nThis is a TypeScript SDK for RingCentral Softphone. It is a complete rewrite of\nthe\n[RingCentral Softphone SDK for JavaScript](https://github.com/ringcentral/ringcentral-softphone-js)\n\nUsers are recommended to use this SDK instead of the JavaScript SDK.\n\nThis SDK allows you to create a softphone without GUI that runs on server-side\nwithout a web browser.\n\n## New documentation and new name\n\nNew documentation is available here:\nhttps://ringcentral.github.io/ringcentral-softphone-ts/\n\nWe are renaming this SDK to **RingCentral Cloud Phone SDK**, and it is currently\na work in progress.\n\n## Installation\n\n```\nyarn install ringcentral-softphone\n```\n\n## Where to get credentials?\n\n### Manually\n\n1. Login to https://service.ringcentral.com\n2. Find the user/extension you want to use\n3. Check the user's \"Devices \u0026 Numbers\"\n4. Find a phone/device that you want to use (Phone type **must** be \"Existing\n   Phone\"), if there is none, you need to create one.\n5. Click the \"Set Up and Provision\" button\n6. Click the link \"Set up manually using SIP\"\n7. You will find \"SIP Domain\", \"Outbound Proxy\", \"User Name\", \"Password\" and\n   \"Authorization ID\"\n\nPlease note that, \"SIP Domain\" name should come without port number. I don't\nknow why it shows a port number on the page. This SDK requires a \"domain\" which\nis \"SIP Domain\" but without the port number.\n\nPlease also note that, not every device/phone can be used with the softphone\nSDK. Some phones/devices with type \"RingCentral Phone app\" cannot be used with\nthe softphone SDK. You will need to have a device/phone with type **\"Exsting\nPhone\"**.\n\n### Programmatically\n\nInvoke this API to list all devices under an extension:\nhttps://developers.ringcentral.com/api-reference/Devices/listExtensionDevices\n\nPlease note that, not every device can be used for this softphone SDK. You will\nneed to find an device with **`type: 'OtherPhone'`**. Devices with\n`type: 'SoftPhone'` can **NOT** be used for this softphone SDK.\n\nI know this is confusing. `type: 'SoftPhone'` in API response is the same as\n`type = \"RingCentral Phone app\"` in the GUI (mentioned in the Manually section\nabove). `type: 'OtherPhone'` in API response is the same as\n`type = \"Exiting Phone\"` in the GUI.\n\nIf you cannot find an appropriate device, you will need to create a device\nmanually. Please refer to the previous section.\n\nInvoke this RESTful API:\nhttps://developers.ringcentral.com/api-reference/Devices/readDeviceSipInfo\n\nPlease note that, in order to invoke this API, you need to be familiar with\nRingCentral RESTful programmming.\n\nHere is a demo:\nhttps://github.com/tylerlong/rc-get-device-info-demo/blob/main/src/demo.ts\n\nThe credentials data returned by that API is like this:\n\n```json\n{\n  \"domain\": \"sip.ringcentral.com\",\n  \"outboundProxies\": [\n    {\n      \"region\": \"EMEA\",\n      \"proxy\": \"sip40.ringcentral.com:5090\",\n      \"proxyTLS\": \"sip40.ringcentral.com:5096\"\n    },\n    {\n      \"region\": \"APAC\",\n      \"proxy\": \"sip71.ringcentral.com:5090\",\n      \"proxyTLS\": \"sip71.ringcentral.com:5096\"\n    },\n    {\n      \"region\": \"NA\",\n      \"proxy\": \"SIP20.ringcentral.com:5090\",\n      \"proxyTLS\": \"sip20.ringcentral.com:5096\"\n    },\n    {\n      \"region\": \"LATAM\",\n      \"proxy\": \"sip80.ringcentral.com:5090\",\n      \"proxyTLS\": \"sip80.ringcentral.com:5096\"\n    }\n    ...\n  ],\n  \"userName\": \"16501234567\",\n  \"password\": \"password\",\n  \"authorizationId\": \"802512345678\"\n}\n```\n\nYou will need to choose a outboundProxy value based on your location. And please\nchoose the `proxyTLS` value because this SDK uses TLS. For example if you live\nin north America, choose `sip10.ringcentral.com:5096`.\n\n## Usage\n\n```ts\nimport Softphone from \"ringcentral-softphone\";\n\nconst softphone = new Softphone({\n  domain: process.env.SIP_INFO_DOMAIN,\n  outboundProxy: process.env.SIP_INFO_OUTBOUND_PROXY,\n  username: process.env.SIP_INFO_USERNAME,\n  password: process.env.SIP_INFO_PASSWORD,\n  authorizationId: process.env.SIP_INFO_AUTHORIZATION_ID,\n});\nawait softphone.register();\n```\n\nFor complete examples, see [demos/](demos/)\n\n## Debug mode\n\n```ts\nsoftphone.enableDebugMode(); // print all SIP messages\n```\n\n## Supported features\n\n- inbound call\n- outbound call\n- inbound DTMF\n- outbound DTMF\n- decline inbound call\n- cancel outbound call\n- hang up ongoing call\n- receive audio stream from peer\n- stream local audio to remote peer\n- call transfer\n- hold / unhold\n\n## inbound call\n\n```ts\nsoftphone.on(\"invite\", async (inviteMessage) =\u003e {\n});\n```\n\n## outbound call\n\n```ts\nconst callSession = await softphone.call(\"12345678987\");\n```\n\n## outbound DTMF\n\n```ts\ncallSession.sendDTMF(\"1\");\n```\n\n### A sugar method to send DTMFs\n\n```ts\nawait callSession.sendDTMFs(\"101#\", 500);\n```\n\nIt will send four chars (1,0,1,#) one by one. After sending each one, it will\npause for 500ms.\n\n## inbound DTMF\n\n```ts\ncallSession.on(\"dtmf\", (digit) =\u003e {\n  console.log(\"dtmf\", digit);\n});\n```\n\n## decline inbound call\n\n```ts\nsoftphone.on(\"invite\", async (inviteMessage) =\u003e {\n  // decline the call\n  // await waitFor({ interval: 1000 });\n  await softphone.decline(inviteMessage);\n}\n```\n\n## cancel outbound call\n\n```ts\ncallSession.cancel();\n```\n\nThis should be invoked BEFORE the call is answered\n\n## hang up ongoing call\n\n```ts\ncallSession.hangup();\n```\n\n## receive audio stream from peer\n\n```ts\nconst writeStream = fs.createWriteStream(`${callSession.callId}.wav`, {\n  flags: \"a\",\n});\ncallSession.on(\"audioPacket\", (rtpPacket: RtpPacket) =\u003e {\n  writeStream.write(rtpPacket.payload);\n});\n// either you or the peer hang up\ncallSession.once(\"disposed\", () =\u003e {\n  writeStream.close();\n});\n```\n\n## stream local audio to remote peer\n\n```ts\n// send audio to remote peer\nconst streamer = callSession.streamAudio(\n  fs.readFileSync(\"demos/opus-48000-2.wav\"),\n);\n// You may subscribe to the 'finished' event of the streamer to know when the audio sending is finished\nstreamer.once(\"finished\", () =\u003e {\n  console.log(\"audio sending finished\");\n});\n\n// // You may loop the streaming:\n// streamer.on(\"finished\", () =\u003e {\n//   streamer.start();\n// })\n\n// // you may pause/resume/stop audio sending at any time\n// await waitFor({ interval: 3000 });\n// streamer.pause();\n// await waitFor({ interval: 3000 });\n// streamer.resume();\n// await waitFor({ interval: 2000 });\n// streamer.stop();\n\n// // you may start/restart the streaming:\n// streamer.start();\n```\n\n## call transfer\n\n```ts\nawait callSession.transfer(\"12345678987\");\n```\n\n## hold / unhold\n\n```ts\nawait callSession.hold();\nawait callSession.unhold();\n```\n\nPlease note that, if you are streaming audio to remote peer, you may want to\npause the streaming when the call is on hold.\n\n## Audio codec\n\n### By default it is `OPUS/16000`\n\n### Other codecs\n\nThere are two more codecs supported: `OPUS/48000/2` and `PCMU/8000`.\n\nTo use them, you will need to explicitly set them when creating the softphone\ninstance:\n\n```ts\nimport Softphone from \"ringcentral-softphone\";\n\nconst softphone = new Softphone({\n  // ...\n  codec: \"PCMU/8000\", // or \"OPUS/48000/2\" or \"OPUS/16000\"\n  // ...\n});\n```\n\n### OPUS/16000\n\nThe codec used between server and client is \"OPUS/16000\". This SDK will auto\ndecode/encode the codec to/from \"uncompressed PCM\".\n\nBit rate is 16, which means 16 bits per sample. Sample rate is 16000, which\nmeans 16000 samples per second. Encoding is \"signed-integer\".\n\nYou may play saved audio by the following command:\n\n```\nplay -t raw -b 16 -r 16000 -e signed-integer test.wav\n```\n\nTo stream an audio file to remote peer, you need to make sure that the audio\nfile is playable by the command above.\n\n#### ffmpeg\n\nIf you prefer ffmpeg, here is the command to play the file:\n\n```\nffplay -autoexit -f s16le -ar 16000 test.wav\n```\n\n#### how to generate audio file for testing\n\nOn macOS:\n\n```\nsay \"Hello world\" -o test.wav --data-format=LEI16@16000\n```\n\nFor Linux and Windows, please do some investigation yourself. Audio file\ngeneration is out of scope of this SDK.\n\n### PCMU/8000\n\nIf you choose this codec, make sure audio is playable using the following\ncommands:\n\n```\nplay -b 8 -r 8000 -e mu-law test.raw\n```\n\nPlease note that, if I name the file as *.wav, `play` will complain:\n\n```\nplay FAIL formats: can't open input file `6fdbbf2f-74fe-437a-b5a7-80c0c546baf0.wav': WAVE: RIFF header not found\n```\n\nEither you rename it to *.raw or use `ffplay` instead\n\n```\nffplay -autoexit -f mulaw -ar 8000 test.wav\n```\n\n### OPUS/48000/2\n\nIf you choose this codec, make sure audio is playable using the following\ncommands:\n\n```\nplay -t raw -b 16 -r 48000 -e signed-integer -c 2 test.wav\n```\n\nI don't know how to use `ffplay` to play such an audio file. Please create a PR\nif you know, thanks.\n\n## Multiple instances with same credentials\n\nYou can run multiple softphone instances with the same credentials without\nencountering any errors. However, only the most recent instance will receive\ninbound calls.\n\nIn the future, we may consider supporting multiple active instances using the\nsame credentials. For now, we believe there is no demand for this functionality.\n\n## Invalid callee number\n\nIf you call an invalid number. The sip server will return \"SIP/2.0 486 Busy\nHere\".\n\nThis SDK will emit a \"busy\" event for the call session and dispose it.\n\nYou can detect such an event by:\n\n```ts\ncallSession.once(\"busy\", () =\u003e {\n  console.log(\"cannot reach the callee number\");\n});\n```\n\n## Pipe a call session to another\n\nWhen you get audio from a call session, you may forward it to another call\nsession:\n\n```ts\ncallSession1.on(\"rtpPacket\", (rtpPacket: RtpPacket) =\u003e {\n  // if statement is to make sure that it is an audio packet\n  if (rtpPacket.header.payloadType === softphone.codec.id) {\n    callSession2.sendPacket(rtpPacket);\n  }\n});\n```\n\n## Telephony Session ID (\u0026 Call Party ID)\n\nFor outbound calls, you will be able to find header like this\n`p-rc-api-ids: party-id=p-a0d17e323f0fez1953f50f90dz296e3440000-1;session-id=s-a0d17e323f0fez1953f50f90dz296e3440000`\nfrom `outbounCallSession.sipMessage.headers`. I have added two sugar methods:\n`outboundCallSession.sessionId` and `outboundCallSession.partyId`.\n\nHowever, for inbound calls, the SIP server doesn't tell us anything about the\nTelephony Session ID. You may use\n[this workaround](https://github.com/tylerlong/rc-softphone-call-id-test).\n\n## 🔧 `ignoreTlsCertErrors` (optional)\n\nMost developers **do not need this option**.\n\nHowever, in rare cases — such as testing in a **lab or development environment**\nwith self-signed or improperly configured TLS certificates — you may encounter\ncertificate validation errors when establishing a TLS connection.\n\nTo bypass these errors, you can set the `ignoreTlsCertErrors` flag to `true`:\n\n```ts\nconst softphone = new Softphone({\n  ...\n  ignoreTlsCertErrors: true\n});\n```\n\n\u003e ⚠️ Warning: Enabling this option disables certificate verification and makes\n\u003e the TLS connection vulnerable to man-in-the-middle (MITM) attacks. Use only in\n\u003e trusted, controlled environments — never in production.\n\n## Troubleshooting (Common issues)\n\n### `SIP/2.0 486 Busy Here` for outbound call\n\nFirst of all, make sure that the target number is valid. If the target number is\ninvalid, you will get `SIP/2.0 486 Busy Here`.\n\nSecondly, make sure that the device has a \"Emergency Address\" configured and\nthere is no complains about Emergency address by checking the details of the\ndevice on https://service.ringcentral.com. It is an known issue that, if the\nEmergency Address is not configured properly, outbound call will not work.\n\n---\n\n## Dev Notes\n\nContent below is for the maintainer/contributor of this SDK.\n\n- We don't need to explicitly tell remote server our local UDP port (for audio\n  streaming) via SIP SDP message. We send a RTP message to the remote server\n  first, so the remote server knows our IP and port. So, the port number in SDP\n  message could be fake.\n- Ref: https://www.ietf.org/rfc/rfc3261.txt\n- Caller Id feature is not supported. `P-Asserted-Identity` doesn't work. I\n  think it is by design, since hardphone cannot support it.\n\n## Conferences\n\nConference involves RESTful API which is out of scope of this SDK. With this\nbeing said, this SDK works well with conferences. Here is a\n[demo project for this SDK work with conferences](https://github.com/tylerlong/softphone-invite-agent-to-conference-demo).\nThe demo is about making a call to a call queue number, it would be even simpler\nif there is no call queue.\n\n#### Code style\n\nWe use `deno fmt \u0026\u0026 deno lint --fix` to format and lint all code.\n\n#### Docs\n\nAll docs related files are located in `mkdocs` folder.\n\nYou will need to setup Python environment and install everything in\n`mkdocs/requirements.txt`.\n\nServe the docs locally: `mkdocs serve -f mkdocs/mkdocs.yml`.\n\nDeploy the docs: `mkdocs gh-deploy -f mkdocs/mkdocs.yml`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringcentral%2Fringcentral-softphone-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fringcentral%2Fringcentral-softphone-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringcentral%2Fringcentral-softphone-ts/lists"}